Leaked source code of windows server 2003
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.

1095 lines
33 KiB

  1. /*
  2. * spell.c
  3. *
  4. * Implementation of spelling
  5. *
  6. * Owner: v-brakol
  7. * bradk@directeq.com
  8. */
  9. #include "pch.hxx"
  10. #define SPID
  11. #include "richedit.h"
  12. #include "resource.h"
  13. #include <mshtml.h>
  14. #include <mshtmcid.h>
  15. #include "mshtmhst.h"
  16. #include <docobj.h>
  17. #include "spell.h"
  18. #include "strconst.h"
  19. #include <options.h>
  20. #include <goptions.h>
  21. #include "mailnews.h"
  22. #include "hotlinks.h"
  23. #include "bodyutil.h"
  24. #include <shlwapi.h>
  25. #include <error.h>
  26. #include "htmlstr.h"
  27. #include "optres.h"
  28. #include "mlang.h"
  29. #include "lid.h"
  30. #include "shlwapip.h"
  31. #include "msi.h"
  32. #include "demand.h"
  33. #ifdef ImageList_GetIcon
  34. #undef ImageList_GetIcon
  35. #endif
  36. #include <shfusion.h>
  37. #define cchMaxPathName (256)
  38. #define TESTHR(hr) (FAILED(hr) || hr == HR_S_ABORT || hr == HR_S_SPELLCANCEL)
  39. #define SPELLER_GUID "{CC29EB3F-7BC2-11D1-A921-00A0C91E2AA2}"
  40. #define DICTIONARY_GUID "{CC29EB3D-7BC2-11D1-A921-00A0C91E2AA2}"
  41. #ifdef DEBUG
  42. #define SPELLER_DEBUG_GUID "{CC29EB3F-7BC2-11D1-A921-10A0C91E2AA2}"
  43. #define DICTIONARY_DEBUG_GUID "{CC29EB3D-7BC2-11D1-A921-10A0C91E2AA2}"
  44. #endif // DEBUG
  45. typedef BOOL (LPFNENUMLANG)(DWORD_PTR, LPTSTR);
  46. typedef BOOL (LPFNENUMUSERDICT)(DWORD_PTR, LPTSTR);
  47. typedef struct _FILLLANG
  48. {
  49. HWND hwndCombo;
  50. BOOL fUnknownFound;
  51. BOOL fDefaultFound;
  52. BOOL fCurrentFound;
  53. UINT lidDefault;
  54. UINT lidCurrent;
  55. } FILLLANG, * LPFILLLANG;
  56. BOOL FDBCSEnabled(void);
  57. BOOL TestLangID(LPCTSTR szLangId);
  58. BOOL GetLangID(LPTSTR szLangID, DWORD cchLangId);
  59. WORD WGetLangID(void);
  60. BOOL SetLangID(LPTSTR szLandID);
  61. DWORD GetSpellingPaths(LPCTSTR szKey, LPTSTR szReturnBuffer, LPTSTR szMdr, UINT cchReturnBufer);
  62. VOID OpenCustomDictionary(VOID);
  63. VOID FillLanguageDropDown(HWND hwndLang);
  64. VOID EnumLanguages(DWORD_PTR, LPFNENUMLANG);
  65. BOOL FindLangCallback(DWORD_PTR dwLangId, LPTSTR lpszLang);
  66. BOOL EnumLangCallback(DWORD_PTR dwLangId, LPTSTR lpszLang);
  67. BOOL FBadSpellChecker(LPSTR rgchBufDigit);
  68. BOOL GetNewSpellerEngine(LANGID lgid, TCHAR *rgchEngine, DWORD cchEngine, TCHAR *rgchLex, DWORD cchLex, BOOL bTestAvail);
  69. HRESULT OpenDirectory(TCHAR *szDir);
  70. ////Spelling tab CS-help
  71. const static HELPMAP g_rgCtxMapSpell[] = {
  72. {CHK_AlwaysSuggest, IDH_NEWS_SPELL_SUGGEST_REPL},
  73. {CHK_CheckSpellingOnSend, IDH_NEWS_SPELL_CHECK_BEFORE_SEND},
  74. {CHK_IgnoreUppercase, IDH_NEWS_SPELL_IGNORE_UPPERCASE},
  75. {CHK_IgnoreNumbers, IDH_NEWS_SPELL_IGNORE_WITH_NUMBERS},
  76. {CHK_IgnoreOriginalMessage, IDH_NEWS_SPELL_ORIGINAL_TEXT},
  77. {CHK_IgnoreURL, IDH_OPTIONS_SPELLING_INTERNET_ADDRESSES},
  78. {idcSpellLanguages, IDH_OPTIONS_SPELLING_LANGUAGE},
  79. {idcViewDictionary, IDH_OPTIONS_SPELLING_DICTIONARY},
  80. {CHK_CheckSpellingOnType, 0},
  81. {idcStatic1, IDH_NEWS_COMM_GROUPBOX},
  82. {idcStatic2, IDH_NEWS_COMM_GROUPBOX},
  83. {idcStatic3, IDH_NEWS_COMM_GROUPBOX},
  84. {idcStatic4, IDH_NEWS_COMM_GROUPBOX},
  85. {idcStatic5, IDH_NEWS_COMM_GROUPBOX},
  86. {idcStatic6, IDH_NEWS_COMM_GROUPBOX},
  87. {IDC_SPELL_SETTINGS_ICON, IDH_NEWS_COMM_GROUPBOX},
  88. {IDC_SPELL_IGNORE_ICON, IDH_NEWS_COMM_GROUPBOX},
  89. {IDC_SPELL_LANGUAGE_ICON, IDH_NEWS_COMM_GROUPBOX},
  90. {0, 0}};
  91. ASSERTDATA
  92. BOOL FIgnoreNumber(void) { return(DwGetOption(OPT_SPELLIGNORENUMBER)); }
  93. BOOL FIgnoreUpper(void) { return(DwGetOption(OPT_SPELLIGNOREUPPER)); }
  94. BOOL FIgnoreDBCS(void) { return(DwGetOption(OPT_SPELLIGNOREDBCS)); }
  95. BOOL FIgnoreProtect(void) { return(DwGetOption(OPT_SPELLIGNOREPROTECT)); }
  96. BOOL FAlwaysSuggest(void) { return(DwGetOption(OPT_SPELLALWAYSSUGGEST)); }
  97. BOOL FCheckOnSend(void) { return(DwGetOption(OPT_SPELLCHECKONSEND)); }
  98. BOOL FIgnoreURL(void) { return(DwGetOption(OPT_SPELLIGNOREURL)); }
  99. typedef struct
  100. {
  101. LPTSTR pszString;
  102. DWORD cchSize;
  103. } STRING_AND_SIZE;
  104. BOOL TestLangID(LPCTSTR szLangId)
  105. {
  106. // check for new speller
  107. {
  108. TCHAR rgchEngine[MAX_PATH];
  109. int cchEngine = sizeof(rgchEngine) / sizeof(rgchEngine[0]);
  110. TCHAR rgchLex[MAX_PATH];
  111. int cchLex = sizeof(rgchLex) / sizeof(rgchLex[0]);
  112. if (GetNewSpellerEngine((LANGID) StrToInt(szLangId), rgchEngine, cchEngine, rgchLex, cchLex, TRUE))
  113. return TRUE;
  114. }
  115. // use the old code to check for an old speller
  116. {
  117. TCHAR rgchBufKeyTest[cchMaxPathName];
  118. TCHAR rgchBufTest[cchMaxPathName];
  119. TCHAR szMdr[cchMaxPathName];
  120. wnsprintf(rgchBufKeyTest, ARRAYSIZE(rgchBufKeyTest), c_szRegSpellKeyDef, szLangId);
  121. if (GetSpellingPaths(rgchBufKeyTest, rgchBufTest, szMdr, sizeof(rgchBufTest)/sizeof(TCHAR)))
  122. return TRUE;
  123. }
  124. return FALSE;
  125. }
  126. BOOL SetLangID(LPTSTR szLangId)
  127. {
  128. return SetOption(OPT_SPELL_LANGID, szLangId, lstrlen(szLangId) + 1, NULL, 0);
  129. }
  130. /*
  131. * GetSpellLangID
  132. *
  133. * Returns the LangID that should be used as the base for all registry
  134. * operations
  135. *
  136. */
  137. BOOL GetLangID(LPTSTR szLangId, DWORD cchLangId)
  138. {
  139. TCHAR rgchBuf[cchMaxPathName];
  140. TCHAR rgchBufKey[cchMaxPathName];
  141. BOOL fRet;
  142. if (GetOption(OPT_SPELL_LANGID, szLangId, cchLangId) != 5)
  143. {
  144. // For Arabic, we should consider all sub-langs also
  145. // since spelling checker for Aarbic uses Saudi Aarbia sub lang
  146. LANGID langid = GetUserDefaultLangID();
  147. if (PRIMARYLANGID(langid) == LANG_ARABIC)
  148. {
  149. langid = MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SAUDI_ARABIA);
  150. }
  151. wnsprintf(szLangId, cchLangId, "%d", langid);
  152. Assert(lstrlen(szLangId) == 4);
  153. }
  154. wnsprintf(rgchBufKey, ARRAYSIZE(rgchBufKey), c_szRegSpellKeyDef, szLangId);
  155. // copy c_szRegSpellProfile to buffer
  156. StrCpyN(rgchBuf, c_szRegSpellProfile, ARRAYSIZE(rgchBuf));
  157. // add key to buffer
  158. StrCatBuff(rgchBuf, rgchBufKey, ARRAYSIZE(rgchBuf));
  159. // and see if it's legit:
  160. if(!(fRet = TestLangID(szLangId)))
  161. {
  162. STRING_AND_SIZE stringAndSize;
  163. stringAndSize.pszString = szLangId;
  164. stringAndSize.cchSize = cchLangId;
  165. // couldn't open it!
  166. // check for other languages that might be installed...
  167. szLangId[0] = 0;
  168. EnumLanguages((DWORD_PTR) &stringAndSize, FindLangCallback);
  169. if(*szLangId == 0)
  170. wnsprintf(szLangId, cchLangId, "%d", GetUserDefaultLangID());
  171. }
  172. fRet = (szLangId[0] != 0) && TestLangID(szLangId);
  173. return fRet;
  174. }
  175. WORD WGetLangID()
  176. {
  177. TCHAR rgchBufDigit[10];
  178. GetLangID(rgchBufDigit, ARRAYSIZE(rgchBufDigit));
  179. return (WORD) StrToInt(rgchBufDigit);
  180. }
  181. BOOL FindLangCallback(DWORD_PTR dwLangId, LPTSTR lpszLang)
  182. {
  183. // dwLangID is long pointer to szLang ID. Copy it and return FALSE
  184. STRING_AND_SIZE * pStringAndSize = (STRING_AND_SIZE *) dwLangId;
  185. if (pStringAndSize && pStringAndSize->pszString)
  186. {
  187. StrCpyN(pStringAndSize->pszString, lpszLang, pStringAndSize->cchSize);
  188. }
  189. return FALSE;
  190. }
  191. BOOL EnumOldSpellerLanguages(DWORD_PTR dwCookie, LPFNENUMLANG pfn)
  192. {
  193. DWORD iKey = 0;
  194. FILETIME ft;
  195. HKEY hkey = NULL;
  196. LONG lRet;
  197. TCHAR szLangId[cchMaxPathName];
  198. DWORD cchLangId;
  199. BOOL fContinue = TRUE;
  200. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegSpellKeyDefRoot, 0, KEY_ENUMERATE_SUB_KEYS, &hkey) == ERROR_SUCCESS)
  201. {
  202. do
  203. {
  204. cchLangId = (cchMaxPathName - 1) * sizeof(TCHAR);
  205. lRet = RegEnumKeyEx(hkey,
  206. iKey++,
  207. szLangId,
  208. &cchLangId,
  209. NULL,
  210. NULL,
  211. NULL,
  212. &ft);
  213. if (lRet != ERROR_SUCCESS || lRet == ERROR_NO_MORE_ITEMS)
  214. break;
  215. // do some quick sanity checking
  216. if (cchLangId != 4 ||
  217. !IsCharAlphaNumeric(szLangId[0]) ||
  218. IsCharAlpha(szLangId[0]))
  219. {
  220. fContinue = TRUE;
  221. }
  222. else
  223. fContinue = (!TestLangID(szLangId) || (*pfn)(dwCookie, szLangId));
  224. } while (fContinue);
  225. }
  226. if (hkey)
  227. RegCloseKey(hkey);
  228. return fContinue;
  229. }
  230. BOOL EnumNewSpellerLanguages(DWORD_PTR dwCookie, LPFNENUMLANG pfn)
  231. {
  232. BOOL fContinue = TRUE;
  233. DWORD i;
  234. UINT installState;
  235. UINT componentState;
  236. TCHAR rgchQualifier[MAX_PATH];
  237. DWORD cchQualifier;
  238. #ifdef DEBUG
  239. for(i=0; fContinue; i++)
  240. {
  241. cchQualifier = sizeof(rgchQualifier) / sizeof(rgchQualifier[0]);
  242. componentState = MsiEnumComponentQualifiers(DICTIONARY_DEBUG_GUID, i, rgchQualifier, &cchQualifier, NULL, NULL);
  243. if (componentState != ERROR_SUCCESS)
  244. break;
  245. // find the language ID
  246. // the string is formatted as 1033\xxxxxx
  247. // or 1042
  248. {
  249. TCHAR szLangId[cchMaxPathName];
  250. TCHAR *pSlash;
  251. StrCpyN(szLangId, rgchQualifier, ARRAYSIZE(szLangId));
  252. pSlash = StrChr(szLangId, '\\');
  253. if (pSlash)
  254. *pSlash = 0;
  255. fContinue = (*pfn)(dwCookie, szLangId);
  256. }
  257. }
  258. #endif // DEBUG
  259. for(i=0; fContinue; i++)
  260. {
  261. cchQualifier = sizeof(rgchQualifier) / sizeof(rgchQualifier[0]);
  262. componentState = MsiEnumComponentQualifiers(DICTIONARY_GUID, i, rgchQualifier, &cchQualifier, NULL, NULL);
  263. if (componentState != ERROR_SUCCESS)
  264. break;
  265. // find the language ID
  266. // the string is formatted as 1033\xxxxxx
  267. // or 1042
  268. {
  269. TCHAR szLangId[cchMaxPathName];
  270. TCHAR *pSlash;
  271. StrCpyN(szLangId, rgchQualifier, ARRAYSIZE(szLangId));
  272. pSlash = StrChr(szLangId, '\\');
  273. if (pSlash)
  274. *pSlash = 0;
  275. fContinue = (*pfn)(dwCookie, szLangId);
  276. }
  277. }
  278. return fContinue;
  279. }
  280. VOID EnumLanguages(DWORD_PTR dwCookie, LPFNENUMLANG pfn)
  281. {
  282. // enum all languages
  283. EnumNewSpellerLanguages(dwCookie, pfn);
  284. EnumOldSpellerLanguages(dwCookie, pfn);
  285. }
  286. /*
  287. * GetSpellingPaths
  288. *
  289. * Purpose:
  290. * Function to get Spelling DLL names.
  291. *
  292. * Arguments:
  293. * szKey c_szRegSpellKeyDef (with correct language)
  294. * szDefault c_szRegSpellEmpty
  295. * szReturnBuffer dll filename
  296. * szMdr dictionary filename
  297. * cchReturnBufer
  298. *
  299. * Returns:
  300. * DWORD
  301. */
  302. DWORD GetSpellingPaths(LPCTSTR szKey, LPTSTR szReturnBuffer, LPTSTR szMdr, UINT cchReturnBufer)
  303. {
  304. DWORD dwRet = 0;
  305. TCHAR rgchBuf[cchMaxPathName];
  306. DWORD dwType, cbData;
  307. HKEY hkey = NULL;
  308. LPTSTR szValue;
  309. szReturnBuffer[0] = 0;
  310. wnsprintf(rgchBuf, ARRAYSIZE(rgchBuf), TEXT("%s%s"), c_szRegSpellProfile, szKey);
  311. if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, rgchBuf, 0, KEY_QUERY_VALUE, &hkey))
  312. goto err;
  313. cbData = cchReturnBufer * sizeof(TCHAR);
  314. szValue = (LPTSTR) (szMdr ? c_szRegSpellPath : c_szRegSpellPathDict);
  315. if (ERROR_SUCCESS != SHQueryValueEx(hkey, szValue, 0L, &dwType, (BYTE *) szReturnBuffer, &cbData))
  316. goto err;
  317. // Parse off the main dictionary filename
  318. if(szMdr)
  319. {
  320. szMdr[0] = 0;
  321. cbData = cchReturnBufer * sizeof(TCHAR);
  322. if (ERROR_SUCCESS != SHQueryValueEx(hkey, c_szRegSpellPathLex, 0L, &dwType, (BYTE *) szMdr, &cbData))
  323. goto err;
  324. }
  325. dwRet = cbData;
  326. err:
  327. if(hkey)
  328. RegCloseKey(hkey);
  329. return dwRet;
  330. }
  331. BOOL GetNewSpellerEngine(LANGID lgid, TCHAR *rgchEngine, DWORD cchEngine, TCHAR *rgchLex, DWORD cchLex, BOOL bTestAvail)
  332. {
  333. DWORD er;
  334. LPCSTR rgpszDictionaryTypes[] = {"Normal", "Consise", "Complete"};
  335. int cDictTypes = sizeof(rgpszDictionaryTypes) / sizeof(LPCSTR);
  336. int i;
  337. TCHAR rgchQual[MAX_PATH];
  338. bool fFound = FALSE;
  339. DWORD cch;
  340. INSTALLUILEVEL iuilOriginal;
  341. if (rgchEngine == NULL || rgchLex == NULL)
  342. return FALSE;
  343. *rgchEngine = 0;
  344. *rgchLex = 0;
  345. wnsprintf(rgchQual, ARRAYSIZE(rgchQual), "%d\\Normal", lgid);
  346. cch = cchEngine;
  347. if (bTestAvail)
  348. {
  349. // Explicitly Turn off internal installer UI
  350. // Eg: A feature is set to "run from CD," and CD is not present - fail silently
  351. // OE Bug 74697
  352. iuilOriginal = MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
  353. }
  354. #ifdef DEBUG
  355. er = MsiProvideQualifiedComponent(SPELLER_DEBUG_GUID, rgchQual, (bTestAvail ? INSTALLMODE_EXISTING : INSTALLMODE_DEFAULT), rgchEngine, &cch);
  356. if ((er != ERROR_SUCCESS) && (er != ERROR_FILE_NOT_FOUND))
  357. {
  358. cch = cchEngine;
  359. er = MsiProvideQualifiedComponent(SPELLER_GUID, rgchQual, (bTestAvail ? INSTALLMODE_EXISTING : INSTALLMODE_DEFAULT), rgchEngine, &cch);
  360. }
  361. #else
  362. er = MsiProvideQualifiedComponent(SPELLER_GUID, rgchQual, (bTestAvail ? INSTALLMODE_EXISTING : INSTALLMODE_DEFAULT), rgchEngine, &cch);
  363. #endif
  364. if ((er != ERROR_SUCCESS) && (er != ERROR_FILE_NOT_FOUND))
  365. {
  366. fFound = FALSE;
  367. goto errorExit;
  368. }
  369. // Hebrew does not have a main lex
  370. #ifdef OLDHEB
  371. if (lgid != lidHebrew)
  372. {
  373. #endif // OLDHEB
  374. for (i = 0; i < cDictTypes; i++)
  375. {
  376. wnsprintf(rgchQual, ARRAYSIZE(rgchQual), "%d\\%s", lgid, rgpszDictionaryTypes[i]);
  377. cch = cchLex;
  378. #ifdef DEBUG
  379. er = MsiProvideQualifiedComponent(DICTIONARY_DEBUG_GUID, rgchQual, (bTestAvail ? INSTALLMODE_EXISTING : INSTALLMODE_DEFAULT), rgchLex, &cch);
  380. if ((er != ERROR_SUCCESS) && (er != ERROR_FILE_NOT_FOUND))
  381. {
  382. cch = cchLex;
  383. er = MsiProvideQualifiedComponent(DICTIONARY_GUID, rgchQual, (bTestAvail ? INSTALLMODE_EXISTING : INSTALLMODE_DEFAULT), rgchLex, &cch);
  384. }
  385. #else // DEBUG
  386. er = MsiProvideQualifiedComponent(DICTIONARY_GUID, rgchQual, (bTestAvail ? INSTALLMODE_EXISTING : INSTALLMODE_DEFAULT), rgchLex, &cch);
  387. #endif // DEBUG
  388. if ((er == ERROR_SUCCESS) || (er == ERROR_FILE_NOT_FOUND))
  389. {
  390. fFound = TRUE;
  391. break;
  392. }
  393. }
  394. #ifdef OLDHEB
  395. }
  396. #endif //OLDDHEB
  397. errorExit:
  398. if (bTestAvail)
  399. {
  400. // Restore original UI Level
  401. MsiSetInternalUI(iuilOriginal, NULL);
  402. }
  403. return fFound;
  404. }
  405. BOOL FIsNewSpellerInstaller()
  406. {
  407. LANGID langid;
  408. TCHAR rgchEngine[MAX_PATH];
  409. int cchEngine = sizeof(rgchEngine) / sizeof(rgchEngine[0]);
  410. TCHAR rgchLex[MAX_PATH];
  411. int cchLex = sizeof(rgchLex) / sizeof(rgchLex[0]);
  412. // first try to load dictionaries for various languages
  413. langid = WGetLangID();
  414. if (!GetNewSpellerEngine(langid, rgchEngine, cchEngine, rgchLex, cchLex, TRUE))
  415. {
  416. langid = GetSystemDefaultLangID();
  417. if (!GetNewSpellerEngine(langid, rgchEngine, cchEngine, rgchLex, cchLex, TRUE))
  418. {
  419. langid = 1033; // bloody cultural imperialists.
  420. if (!GetNewSpellerEngine(langid, rgchEngine, cchEngine, rgchLex, cchLex, TRUE))
  421. return FALSE;
  422. }
  423. }
  424. return TRUE;
  425. }
  426. /*
  427. * FIsSpellingInstalled
  428. *
  429. * Purpose:
  430. * Is the spelling stuff installed
  431. *
  432. * Arguments:
  433. * none
  434. *
  435. * Returns:
  436. * BOOL Returns TRUE if spelling is installed, else FALSE.
  437. */
  438. BOOL FIsSpellingInstalled()
  439. {
  440. TCHAR rgchBufDigit[10];
  441. if (GetLangID(rgchBufDigit, sizeof(rgchBufDigit)/sizeof(TCHAR)) && !FBadSpellChecker(rgchBufDigit))
  442. return true;
  443. if (FIsNewSpellerInstaller())
  444. return true;
  445. return false;
  446. }
  447. // Does a quick check to see if spelling is available; caches result.
  448. BOOL FCheckSpellAvail(void)
  449. {
  450. static int fSpellAvailable = -1;
  451. if (fSpellAvailable < 0)
  452. fSpellAvailable = (FIsSpellingInstalled() ? 1 : 0);
  453. return (fSpellAvailable > 0);
  454. }
  455. BOOL FDBCSEnabled(void)
  456. {
  457. static int fDBCS = -1;
  458. if (fDBCS < 0)
  459. fDBCS = GetSystemMetrics(SM_DBCSENABLED);
  460. return (fDBCS > 0);
  461. }
  462. // Fill the options list with the available spelling languages
  463. VOID FillLanguageDropDown(HWND hwndLang)
  464. {
  465. TCHAR rgchBuf[cchMaxPathName];
  466. FILLLANG fl;
  467. int i;
  468. // get the current language
  469. GetLangID(rgchBuf, cchMaxPathName);
  470. fl.hwndCombo = hwndLang;
  471. fl.fUnknownFound = FALSE;
  472. fl.fDefaultFound = FALSE;
  473. fl.fCurrentFound = FALSE;
  474. fl.lidDefault = WGetLangID();
  475. fl.lidCurrent = StrToInt(rgchBuf);
  476. EnumLanguages((DWORD_PTR) &fl, EnumLangCallback);
  477. // this should never happen, but just in case
  478. if (!fl.fDefaultFound)
  479. {
  480. LoadString(g_hLocRes, idsDefaultLang, rgchBuf, cchMaxPathName - 1);
  481. i = ComboBox_AddString(hwndLang, rgchBuf);
  482. ComboBox_SetItemData(hwndLang, i, fl.lidDefault);
  483. }
  484. // select the current one, if found, else the default one.
  485. for (i = ComboBox_GetCount(hwndLang) - 1; i >= 0; i--)
  486. {
  487. UINT lid = (UINT) ComboBox_GetItemData(hwndLang, i);
  488. if ((fl.fCurrentFound && lid == fl.lidCurrent) ||
  489. (!fl.fCurrentFound && fl.fDefaultFound && lid == fl.lidDefault))
  490. {
  491. ComboBox_SetCurSel(hwndLang, i);
  492. break;
  493. }
  494. }
  495. }
  496. BOOL EnumLangCallback(DWORD_PTR dw, LPTSTR lpszLang)
  497. {
  498. LPFILLLANG lpfl = (LPFILLLANG) dw;
  499. TCHAR szLang[cchMaxPathName];
  500. LID lidLang = (LID) StrToInt(lpszLang);
  501. int i;
  502. HRESULT hr=S_OK;
  503. IMultiLanguage2 *pMLang2 = NULL;
  504. RFC1766INFO info;
  505. // check to see if we already have the LID in the ComboBox
  506. {
  507. for (i = ComboBox_GetCount(lpfl->hwndCombo) - 1; i >= 0; i--)
  508. {
  509. LID lid = (UINT) ComboBox_GetItemData(lpfl->hwndCombo, i);
  510. if (lid == lidLang)
  511. return TRUE;
  512. }
  513. }
  514. // Try to create an IMultiLanguage2 interface
  515. hr = CoCreateInstance(CLSID_CMultiLanguage, NULL,CLSCTX_INPROC, IID_IMultiLanguage2, (LPVOID *) &pMLang2);
  516. if (SUCCEEDED(hr))
  517. hr = pMLang2->GetRfc1766Info(MAKELCID(lidLang, SORT_DEFAULT), MLGetUILanguage(), &info);
  518. SafeRelease(pMLang2);
  519. if (SUCCEEDED(hr))
  520. {
  521. if (WideCharToMultiByte (CP_ACP, 0, info.wszLocaleName, -1,
  522. szLang, sizeof(szLang), NULL, NULL))
  523. {
  524. szLang[ARRAYSIZE(szLang) - 1] = '\0'; //better safe than not null terminated
  525. i = ComboBox_AddString(lpfl->hwndCombo, szLang);
  526. ComboBox_SetItemData(lpfl->hwndCombo, i, lidLang);
  527. if (lidLang == lpfl->lidDefault)
  528. lpfl->fDefaultFound = TRUE;
  529. if (lidLang == lpfl->lidCurrent)
  530. lpfl->fCurrentFound = TRUE;
  531. return TRUE;
  532. }
  533. }
  534. return TRUE; // keep enumerating
  535. }
  536. INT_PTR CALLBACK SpellingPageProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  537. {
  538. BOOL f;
  539. OPTINFO *pmoi;
  540. pmoi = (OPTINFO *)GetWindowLongPtr(hwnd, DWLP_USER);
  541. switch (message)
  542. {
  543. case WM_INITDIALOG:
  544. {
  545. UINT uCP;
  546. Assert(pmoi == NULL);
  547. pmoi = (OPTINFO *)(((PROPSHEETPAGE *)lParam)->lParam);
  548. Assert(pmoi != NULL);
  549. SetWindowLongPtr(hwnd, DWLP_USER, (LPARAM)pmoi);
  550. ButtonChkFromOptInfo(hwnd, CHK_AlwaysSuggest, pmoi, OPT_SPELLALWAYSSUGGEST);
  551. ButtonChkFromOptInfo(hwnd, CHK_CheckSpellingOnSend, pmoi, OPT_SPELLCHECKONSEND);
  552. ButtonChkFromOptInfo(hwnd, CHK_CheckSpellingOnType, pmoi, OPT_SPELLCHECKONTYPE);
  553. ButtonChkFromOptInfo(hwnd, CHK_IgnoreUppercase, pmoi, OPT_SPELLIGNOREUPPER);
  554. ButtonChkFromOptInfo(hwnd, CHK_IgnoreNumbers, pmoi, OPT_SPELLIGNORENUMBER);
  555. ButtonChkFromOptInfo(hwnd, CHK_IgnoreOriginalMessage, pmoi, OPT_SPELLIGNOREPROTECT);
  556. ButtonChkFromOptInfo(hwnd, CHK_IgnoreDBCS, pmoi, OPT_SPELLIGNOREDBCS);
  557. ButtonChkFromOptInfo(hwnd, CHK_IgnoreURL, pmoi, OPT_SPELLIGNOREURL);
  558. FillLanguageDropDown(GetDlgItem(hwnd, idcSpellLanguages));
  559. uCP = GetACP();
  560. // 50406: if we're not DBCS or (we're Japaneese or either Chinese) don't show
  561. // the DBCS option.
  562. if (!FDBCSEnabled() || ((932==uCP) || (936==uCP) || (950==uCP)))
  563. {
  564. ShowWindow(GetDlgItem(hwnd, CHK_IgnoreDBCS), SW_HIDE);
  565. EnableWindow(GetDlgItem(hwnd, CHK_IgnoreDBCS), FALSE);
  566. }
  567. // Pictures
  568. HICON hIcon;
  569. hIcon = ImageList_GetIcon(pmoi->himl, ID_SPELL, ILD_TRANSPARENT);
  570. SendDlgItemMessage(hwnd, IDC_SPELL_SETTINGS_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM) hIcon);
  571. hIcon = ImageList_GetIcon(pmoi->himl, ID_SPELL_IGNORE, ILD_TRANSPARENT);
  572. SendDlgItemMessage(hwnd, IDC_SPELL_IGNORE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM) hIcon);
  573. hIcon = ImageList_GetIcon(pmoi->himl, ID_LANGUAGE_ICON, ILD_TRANSPARENT);
  574. SendDlgItemMessage(hwnd, IDC_SPELL_LANGUAGE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM) hIcon);
  575. SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)1);
  576. }
  577. return(TRUE);
  578. case WM_HELP:
  579. case WM_CONTEXTMENU:
  580. return OnContextHelp(hwnd, message, wParam, lParam, g_rgCtxMapSpell);
  581. case WM_COMMAND:
  582. if (1 != GetWindowLongPtr(hwnd, GWLP_USERDATA))
  583. break;
  584. if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == CBN_SELCHANGE)
  585. {
  586. if (LOWORD(wParam) == idcViewDictionary)
  587. {
  588. AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsSpellCaption), MAKEINTRESOURCEW(idsErrSpellWarnDictionary), NULL, MB_OK | MB_ICONINFORMATION);
  589. OpenCustomDictionary();
  590. }
  591. else
  592. {
  593. PropSheet_Changed(GetParent(hwnd), hwnd);
  594. }
  595. }
  596. break;
  597. case WM_NOTIFY:
  598. switch (((NMHDR FAR *)lParam)->code)
  599. {
  600. case PSN_APPLY:
  601. {
  602. int i;
  603. int lidNew;
  604. int lidOld;
  605. TCHAR rgchBuf[10];
  606. // get the current language
  607. GetLangID(rgchBuf, sizeof(rgchBuf) / sizeof(TCHAR));
  608. lidOld = StrToInt(rgchBuf);
  609. Assert(pmoi != NULL);
  610. ButtonChkToOptInfo(hwnd, CHK_AlwaysSuggest, pmoi, OPT_SPELLALWAYSSUGGEST);
  611. ButtonChkToOptInfo(hwnd, CHK_CheckSpellingOnSend, pmoi, OPT_SPELLCHECKONSEND);
  612. ButtonChkToOptInfo(hwnd, CHK_CheckSpellingOnType, pmoi, OPT_SPELLCHECKONTYPE);
  613. ButtonChkToOptInfo(hwnd, CHK_IgnoreUppercase, pmoi, OPT_SPELLIGNOREUPPER);
  614. ButtonChkToOptInfo(hwnd, CHK_IgnoreNumbers, pmoi, OPT_SPELLIGNORENUMBER);
  615. ButtonChkToOptInfo(hwnd, CHK_IgnoreOriginalMessage, pmoi, OPT_SPELLIGNOREPROTECT);
  616. ButtonChkToOptInfo(hwnd, CHK_IgnoreDBCS, pmoi, OPT_SPELLIGNOREDBCS);
  617. ButtonChkToOptInfo(hwnd, CHK_IgnoreURL, pmoi, OPT_SPELLIGNOREURL);
  618. i = ComboBox_GetCurSel(GetDlgItem(hwnd, idcSpellLanguages));
  619. lidNew =(LID) ComboBox_GetItemData(GetDlgItem(hwnd, idcSpellLanguages), i);
  620. if (lidNew != lidOld)
  621. {
  622. wnsprintf(rgchBuf, ARRAYSIZE(rgchBuf), "%d", lidNew);
  623. SetLangID(rgchBuf);
  624. }
  625. }
  626. break;
  627. }
  628. break;
  629. case WM_DESTROY:
  630. FreeIcon(hwnd, IDC_SPELL_SETTINGS_ICON);
  631. FreeIcon(hwnd, IDC_SPELL_IGNORE_ICON);
  632. FreeIcon(hwnd, IDC_SPELL_LANGUAGE_ICON);
  633. break;
  634. #if 0
  635. case WM_HELP:
  636. {
  637. NMHDR nmhdr;
  638. nmhdr.code = PSN_HELP;
  639. SendMessage(hwnd, WM_NOTIFY, 0, (LPARAM) &nmhdr);
  640. SetWindowLong(hwnd, DWL_MSGRESULT, TRUE);
  641. return TRUE;
  642. }
  643. #endif
  644. }
  645. return(FALSE);
  646. }
  647. BOOL EnumOffice9UserDictionaries(DWORD_PTR dwCookie, LPFNENUMUSERDICT pfn)
  648. {
  649. TCHAR rgchBuf[cchMaxPathName];
  650. HKEY hkey = NULL;
  651. FILETIME ft;
  652. DWORD iKey = 0;
  653. LONG lRet;
  654. TCHAR szValue[cchMaxPathName];
  655. DWORD cchValue;
  656. TCHAR szCustDict[cchMaxPathName];
  657. DWORD cchCustDict;
  658. BOOL fContinue = TRUE;
  659. BOOL fFoundUserDict = FALSE;
  660. TCHAR szOffice9Proof[cchMaxPathName]={0};
  661. // SOFTWARE\\Microsoft\\Shared Tools\\Proofing Tools\\Custom Dictionaries
  662. StrCpyN(rgchBuf, c_szRegSpellProfile, ARRAYSIZE(rgchBuf));
  663. StrCatBuff(rgchBuf, c_szRegSpellKeyCustom, ARRAYSIZE(rgchBuf));
  664. if(RegOpenKeyEx(HKEY_CURRENT_USER, rgchBuf, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS)
  665. {
  666. do
  667. {
  668. cchValue = sizeof(szValue) / sizeof(szValue[0]);
  669. cchCustDict = sizeof(szCustDict) / sizeof(szCustDict[0]);
  670. lRet = RegEnumValue(hkey,
  671. iKey++,
  672. szValue,
  673. &cchValue,
  674. NULL,
  675. NULL,
  676. (LPBYTE)szCustDict,
  677. &cchCustDict);
  678. if (lRet != ERROR_SUCCESS || lRet == ERROR_NO_MORE_ITEMS)
  679. break;
  680. fFoundUserDict = TRUE;
  681. // check to see if we have a path
  682. if (!(StrChr(szCustDict, ':') || StrChr(szCustDict, '\\')))
  683. {
  684. TCHAR szTemp[cchMaxPathName];
  685. if (!strlen(szOffice9Proof))
  686. {
  687. LPITEMIDLIST pidl;
  688. if (S_OK == SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, &pidl))
  689. SHGetPathFromIDList(pidl, szOffice9Proof);
  690. // if this fails then we will try the current path
  691. }
  692. StrCpyN(szTemp, szOffice9Proof, ARRAYSIZE(szTemp));
  693. PathAppend(szTemp, c_szSpellOffice9ProofPath);
  694. StrCatBuff(szTemp, szCustDict, ARRAYSIZE(szTemp));
  695. StrCpyN(szCustDict, szTemp, ARRAYSIZE(szCustDict));
  696. }
  697. fContinue = (*pfn)(dwCookie, szCustDict);
  698. } while (fContinue);
  699. }
  700. if (hkey)
  701. RegCloseKey(hkey);
  702. return fFoundUserDict;
  703. }
  704. BOOL EnumOfficeUserDictionaries(DWORD_PTR dwCookie, LPFNENUMUSERDICT pfn)
  705. {
  706. TCHAR rgchBuf[cchMaxPathName];
  707. HKEY hkey = NULL;
  708. FILETIME ft;
  709. DWORD iKey = 0;
  710. LONG lRet;
  711. TCHAR szValue[cchMaxPathName];
  712. DWORD cchValue;
  713. TCHAR szCustDict[cchMaxPathName];
  714. DWORD cchCustDict;
  715. BOOL fFoundUserDict = FALSE;
  716. BOOL fContinue = TRUE;
  717. // SOFTWARE\\Microsoft\\Shared Tools\\Proofing Tools\\Custom Dictionaries
  718. StrCpyN(rgchBuf, c_szRegSpellProfile, ARRAYSIZE(rgchBuf));
  719. StrCatBuff(rgchBuf, c_szRegSpellKeyCustom, ARRAYSIZE(rgchBuf));
  720. if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, rgchBuf, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS)
  721. {
  722. do
  723. {
  724. cchValue = sizeof(szValue) / sizeof(szValue[0]);
  725. cchCustDict = sizeof(szCustDict) / sizeof(szCustDict[0]);
  726. lRet = RegEnumValue(hkey,
  727. iKey++,
  728. szValue,
  729. &cchValue,
  730. NULL,
  731. NULL,
  732. (LPBYTE)szCustDict,
  733. &cchCustDict);
  734. if (lRet != ERROR_SUCCESS || lRet == ERROR_NO_MORE_ITEMS)
  735. break;
  736. fFoundUserDict = TRUE;
  737. fContinue = (*pfn)(dwCookie, szCustDict);
  738. } while (fContinue);
  739. }
  740. if (hkey)
  741. RegCloseKey(hkey);
  742. return fFoundUserDict;
  743. }
  744. VOID EnumUserDictionaries(DWORD_PTR dwCookie, LPFNENUMUSERDICT pfn)
  745. {
  746. // check for Office9 user dictionaries. If we find any
  747. // we bail.
  748. if (EnumOffice9UserDictionaries(dwCookie, pfn))
  749. return;
  750. EnumOfficeUserDictionaries(dwCookie, pfn);
  751. }
  752. BOOL GetDefaultUserDictionary(TCHAR *rgchUserDict, int cchBuff)
  753. {
  754. DWORD dwType;
  755. DWORD cchUserDict;
  756. HKEY hkey = NULL;
  757. BOOL fFound = FALSE;
  758. if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegSharedTools, 0, KEY_QUERY_VALUE, &hkey))
  759. {
  760. cchUserDict = cchBuff;
  761. if (SHQueryValueEx(hkey, c_szRegSharedToolsPath, 0L, &dwType, rgchUserDict, &cchUserDict) == ERROR_SUCCESS)
  762. {
  763. StrCatBuff(rgchUserDict, c_szRegDefCustomDict, cchBuff);
  764. fFound = TRUE;
  765. }
  766. RegCloseKey(hkey);
  767. }
  768. // if we where able to create a path to the user dict store it in the regdb
  769. if (fFound)
  770. {
  771. TCHAR rgchBuf[cchMaxPathName];
  772. StrCpyN(rgchBuf, c_szRegSpellProfile, ARRAYSIZE(rgchBuf));
  773. StrCatBuff(rgchBuf, c_szRegSpellKeyCustom, ARRAYSIZE(rgchBuf));
  774. if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, rgchBuf, 0, rgchBuf, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &hkey, NULL) == ERROR_SUCCESS)
  775. {
  776. RegSetValueEx(hkey, c_szRegSpellPathDict, 0, REG_SZ, (BYTE *)rgchUserDict, (lstrlen(rgchUserDict) + 1) * sizeof(TCHAR));
  777. RegCloseKey(hkey);
  778. }
  779. }
  780. return fFound;
  781. }
  782. BOOL EnumUserDictCallback(DWORD_PTR dwCookie, LPTSTR lpszDict)
  783. {
  784. STRING_AND_SIZE * pStringAndSize = (STRING_AND_SIZE *) dwCookie;
  785. if (pStringAndSize && pStringAndSize->pszString)
  786. {
  787. StrCpyN(pStringAndSize->pszString, lpszDict, pStringAndSize->cchSize);
  788. }
  789. return FALSE;
  790. }
  791. BOOL GetDefUserDictionaries(LPTSTR lpszDict, DWORD cchDict)
  792. {
  793. STRING_AND_SIZE stringAndSize;
  794. stringAndSize.pszString = lpszDict;
  795. stringAndSize.cchSize = cchDict;
  796. lpszDict[0] = 0;
  797. EnumUserDictionaries((DWORD_PTR)&stringAndSize, EnumUserDictCallback);
  798. if (strlen(lpszDict))
  799. return TRUE;
  800. if (GetDefaultUserDictionary(lpszDict, cchDict))
  801. return TRUE;
  802. return FALSE;
  803. }
  804. VOID OpenCustomDictionary(VOID)
  805. {
  806. HKEY hkey = NULL;
  807. TCHAR rgchBuf[cchMaxPathName];
  808. DWORD cbData = 0;
  809. DWORD dwType;
  810. // Verify that .DIC files can be handled:
  811. rgchBuf[0] = '\0';
  812. if (RegOpenKeyEx(HKEY_CLASSES_ROOT, c_szRegDICHandlerKEY, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS)
  813. {
  814. if (hkey)
  815. {
  816. SHQueryValueEx(hkey, NULL, 0L, &dwType, (BYTE *) rgchBuf, &cbData);
  817. RegCloseKey(hkey);
  818. }
  819. }
  820. if (cbData == 0 || !rgchBuf[0])
  821. {
  822. if (RegCreateKeyEx(HKEY_CLASSES_ROOT,
  823. c_szRegDICHandlerKEY,
  824. 0,
  825. rgchBuf,
  826. REG_OPTION_NON_VOLATILE,
  827. KEY_WRITE,
  828. 0,
  829. &hkey,
  830. NULL) == ERROR_SUCCESS)
  831. {
  832. if (hkey)
  833. {
  834. RegSetValueEx(hkey, NULL, 0L, REG_SZ, (BYTE *) c_szRegDICHandlerDefault, (lstrlen(c_szRegDICHandlerDefault) + 1) * sizeof(TCHAR));
  835. RegCloseKey(hkey);
  836. }
  837. }
  838. }
  839. if (GetDefUserDictionaries(rgchBuf, sizeof(rgchBuf)/sizeof(TCHAR)))
  840. {
  841. // make sure our directory exists
  842. {
  843. TCHAR rgchDictDir[MAX_PATH];
  844. StrCpyN(rgchDictDir, rgchBuf, ARRAYSIZE(rgchDictDir));
  845. PathRemoveFileSpec(rgchDictDir);
  846. OpenDirectory(rgchDictDir);
  847. }
  848. // now make sure the file exists
  849. // if it does not create it
  850. {
  851. HANDLE hFile;
  852. hFile = CreateFile(rgchBuf, GENERIC_READ | GENERIC_WRITE, 0, NULL,
  853. CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
  854. if (hFile != INVALID_HANDLE_VALUE)
  855. CloseHandle(hFile);
  856. }
  857. {
  858. SHELLEXECUTEINFO see;
  859. ZeroMemory(&see, sizeof(SHELLEXECUTEINFO));
  860. see.cbSize = sizeof(see);
  861. see.fMask = SEE_MASK_NOCLOSEPROCESS;
  862. see.lpFile = rgchBuf;
  863. see.nShow = SW_SHOWNORMAL;
  864. if (ShellExecuteEx(&see))
  865. {
  866. Assert(see.hProcess);
  867. WaitForInputIdle(see.hProcess, 20000);
  868. CloseHandle(see.hProcess);
  869. }
  870. }
  871. }
  872. }
  873. BOOL FBadSpellChecker(LPSTR rgchBufDigit)
  874. {
  875. TCHAR rgchBufKey[cchMaxPathName];
  876. TCHAR rgchBuf[cchMaxPathName];
  877. TCHAR szMdr[cchMaxPathName];
  878. LPSTR pszSpell;
  879. wnsprintf(rgchBufKey, ARRAYSIZE(rgchBufKey), c_szRegSpellKeyDef, rgchBufDigit);
  880. if (!GetSpellingPaths(rgchBufKey, rgchBuf, szMdr, sizeof(rgchBuf)/sizeof(TCHAR)))
  881. return TRUE;
  882. pszSpell = PathFindFileNameA(rgchBuf);
  883. if (!pszSpell)
  884. return TRUE;
  885. if (lstrcmpi(pszSpell, "msspell.dll")==0 ||
  886. lstrcmpi(pszSpell, "mssp32.dll")==0)
  887. return TRUE;
  888. // [email protected] - check that the dict exists (also check the spell dll
  889. // for good measure) - 40081
  890. // spell dll must exist
  891. if (!PathFileExists(rgchBuf))
  892. return TRUE;
  893. // main dict must exist
  894. if (!PathFileExists(szMdr))
  895. return TRUE;
  896. return FALSE;
  897. }