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.

4249 lines
114 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. #include "richedit.h"
  11. #include "resource.h"
  12. #include "util.h"
  13. #include <mshtml.h>
  14. #include <mshtmcid.h>
  15. #include "mshtmhst.h"
  16. #include "mshtmdid.h"
  17. #include <docobj.h>
  18. #include "spell.h"
  19. #include "strconst.h"
  20. #include "bodyutil.h"
  21. #include <error.h>
  22. #include "htmlstr.h"
  23. #include "dllmain.h"
  24. #include "msi.h"
  25. #include "lid.h"
  26. #include <tchar.h>
  27. #include "demand.h"
  28. #define GetAddr(var, cast, name) {if ((var = (cast)GetProcAddress(m_hinstDll, name)) == NULL) \
  29. goto error;}
  30. #define TESTHR(hr) (FAILED(hr) || hr == HR_S_ABORT || hr == HR_S_SPELLCANCEL)
  31. #define SPELLER_GUID "{CC29EB3F-7BC2-11D1-A921-00A0C91E2AA2}"
  32. #define DICTIONARY_GUID "{CC29EB3D-7BC2-11D1-A921-00A0C91E2AA2}"
  33. #define CSAPI3T1_GUID "{CC29EB41-7BC2-11D1-A921-00A0C91E2AA2}"
  34. #ifdef DEBUG
  35. #define SPELLER_DEBUG_GUID "{CC29EB3F-7BC2-11D1-A921-10A0C91E2AA2}"
  36. #define DICTIONARY_DEBUG_GUID "{CC29EB3D-7BC2-11D1-A921-10A0C91E2AA2}"
  37. #define CSAPI3T1_DEBUG_GUID "{CC29EB41-7BC2-11D1-A921-10A0C91E2AA2}"
  38. #endif // DEBUG
  39. #define MAX_SPELLWORDS 10
  40. typedef BOOL (LPFNENUMLANG)(DWORD_PTR, LPTSTR);
  41. typedef BOOL (LPFNENUMUSERDICT)(DWORD_PTR, LPTSTR);
  42. typedef struct _FILLLANG
  43. {
  44. HWND hwndCombo;
  45. BOOL fUnknownFound;
  46. BOOL fDefaultFound;
  47. BOOL fCurrentFound;
  48. UINT lidDefault;
  49. UINT lidCurrent;
  50. } FILLLANG, * LPFILLLANG;
  51. BOOL TestLangID(LPCTSTR szLangId);
  52. BOOL GetLangID(IOleCommandTarget* pParentCmdTarget, LPTSTR szLangID, DWORD cchLangId);
  53. WORD WGetLangID(IOleCommandTarget* pParentCmdTarget);
  54. DWORD GetSpellingPaths(LPCTSTR szKey, LPTSTR szReturnBuffer, LPTSTR szMdr, UINT cchReturnBufer);
  55. VOID EnumLanguages(DWORD_PTR, LPFNENUMLANG);
  56. BOOL FindLangCallback(DWORD_PTR dwLangId, LPTSTR lpszLang);
  57. void RemoveTrailingSpace(LPTSTR lpszWord);
  58. void DumpRange(IHTMLTxtRange *pRange);
  59. BOOL FBadSpellChecker(LPSTR rgchBufDigit);
  60. void EnableRepeatedWindows(CSpell* pSpell, HWND hwndDlg);
  61. BOOL GetNewSpellerEngine(LANGID lgid, TCHAR *rgchEngine, DWORD cchEngine, TCHAR *rgchLex, DWORD cchLex, BOOL bTestAvail);
  62. VOID EnumUserDictionaries(DWORD_PTR dwCookie, LPFNENUMUSERDICT pfn);
  63. BOOL GetDefaultUserDictionary(TCHAR *rgchUserDict, int cchBuff);
  64. WORD GetWCharType(WCHAR wc);
  65. HRESULT OpenDirectory(TCHAR *szDir);
  66. BOOL TestLangID(LPCTSTR szLangId)
  67. {
  68. // check for new speller
  69. {
  70. TCHAR rgchEngine[MAX_PATH];
  71. int cchEngine = sizeof(rgchEngine) / sizeof(rgchEngine[0]);
  72. TCHAR rgchLex[MAX_PATH];
  73. int cchLex = sizeof(rgchLex) / sizeof(rgchLex[0]);
  74. if (GetNewSpellerEngine((USHORT) StrToInt(szLangId), rgchEngine, cchEngine, rgchLex, cchLex, TRUE))
  75. return TRUE;
  76. }
  77. // use the old code to check for an old speller
  78. {
  79. TCHAR rgchBufKeyTest[cchMaxPathName];
  80. TCHAR rgchBufTest[cchMaxPathName];
  81. TCHAR szMdr[cchMaxPathName];
  82. wsprintf(rgchBufKeyTest, c_szRegSpellKeyDef, szLangId);
  83. if (GetSpellingPaths(rgchBufKeyTest, rgchBufTest, szMdr, sizeof(rgchBufTest)/sizeof(TCHAR)))
  84. return TRUE;
  85. }
  86. return FALSE;
  87. }
  88. /*
  89. * GetSpellLangID
  90. *
  91. * Returns the LangID that should be used as the base for all registry
  92. * operations
  93. *
  94. */
  95. BOOL GetLangID(IOleCommandTarget* pParentCmdTarget, LPTSTR szLangId, DWORD cchLangId)
  96. {
  97. TCHAR rgchBuf[cchMaxPathName];
  98. TCHAR rgchBufKey[cchMaxPathName];
  99. BOOL fRet;
  100. VARIANT va;
  101. szLangId[0] = 0;
  102. Assert(pParentCmdTarget);
  103. if (pParentCmdTarget && pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_SPELL_LANGUAGE, 0, NULL, &va)== S_OK)
  104. {
  105. TCHAR rgchLangId[cchMaxPathName];
  106. if (WideCharToMultiByte (CP_ACP, 0, va.bstrVal, -1,
  107. rgchLangId, sizeof(rgchLangId), NULL, NULL))
  108. {
  109. strcpy(szLangId, rgchLangId);
  110. }
  111. SysFreeString(va.bstrVal);
  112. }
  113. if (*szLangId == 0)
  114. {
  115. wsprintf(szLangId, "%d", GetUserDefaultLangID());
  116. Assert(lstrlen(szLangId) == 4);
  117. }
  118. wsprintf(rgchBufKey, c_szRegSpellKeyDef, szLangId);
  119. // copy c_szRegSpellProfile to buffer
  120. lstrcpy(rgchBuf, c_szRegSpellProfile);
  121. // add key to buffer
  122. lstrcat(rgchBuf, rgchBufKey);
  123. // and see if it's legit:
  124. if(!(fRet = TestLangID(szLangId)))
  125. {
  126. // couldn't open it!
  127. // check for other languages that might be installed...
  128. szLangId[0] = 0;
  129. EnumLanguages((DWORD_PTR) szLangId, FindLangCallback);
  130. if(*szLangId == 0)
  131. wsprintf(szLangId, "%d", GetUserDefaultLangID());
  132. }
  133. fRet = (szLangId[0] != 0) && TestLangID(szLangId);
  134. return fRet;
  135. }
  136. WORD WGetLangID(IOleCommandTarget* pParentCmdTarget)
  137. {
  138. TCHAR rgchBufDigit[10];
  139. if (!GetLangID(pParentCmdTarget, rgchBufDigit, sizeof(rgchBufDigit)/sizeof(TCHAR)))
  140. return GetUserDefaultLangID();
  141. return (WORD) StrToInt(rgchBufDigit);
  142. }
  143. BOOL FindLangCallback(DWORD_PTR dwLangId, LPTSTR lpszLang)
  144. {
  145. // dwLangID is long pointer to szLang ID. Copy it and return FALSE
  146. lstrcpy((LPTSTR) dwLangId, lpszLang);
  147. return FALSE;
  148. }
  149. BOOL EnumOldSpellerLanguages(DWORD_PTR dwCookie, LPFNENUMLANG pfn)
  150. {
  151. DWORD iKey = 0;
  152. FILETIME ft;
  153. HKEY hkey = NULL;
  154. LONG lRet;
  155. TCHAR szLangId[cchMaxPathName];
  156. DWORD cchLangId;
  157. BOOL fContinue;
  158. // [email protected] - changed KEY_QUERY_VALUE to KEY_ENUMERATE_SUB_KEYS - 26203
  159. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegSpellKeyDefRoot, 0, KEY_ENUMERATE_SUB_KEYS, &hkey) == ERROR_SUCCESS)
  160. {
  161. do
  162. {
  163. cchLangId = (cchMaxPathName - 1) * sizeof(TCHAR);
  164. lRet = RegEnumKeyEx(hkey,
  165. iKey++,
  166. szLangId,
  167. &cchLangId,
  168. NULL,
  169. NULL,
  170. NULL,
  171. &ft);
  172. if (lRet != ERROR_SUCCESS || lRet == ERROR_NO_MORE_ITEMS)
  173. break;
  174. // do some quick sanity checking
  175. if (cchLangId != 4 ||
  176. !IsCharAlphaNumeric(szLangId[0]) ||
  177. IsCharAlpha(szLangId[0]))
  178. {
  179. fContinue = TRUE;
  180. }
  181. else
  182. fContinue = (!TestLangID(szLangId) || (*pfn)(dwCookie, szLangId));
  183. } while (fContinue);
  184. }
  185. if (hkey)
  186. RegCloseKey(hkey);
  187. return fContinue;
  188. }
  189. BOOL EnumNewSpellerLanguages(DWORD_PTR dwCookie, LPFNENUMLANG pfn)
  190. {
  191. BOOL fContinue = TRUE;
  192. DWORD i;
  193. UINT installState;
  194. UINT componentState;
  195. TCHAR rgchQualifier[MAX_PATH];
  196. DWORD cchQualifier;
  197. #ifdef DEBUG
  198. for(i=0; fContinue; i++)
  199. {
  200. cchQualifier = sizeof(rgchQualifier) / sizeof(rgchQualifier[0]);
  201. componentState = MsiEnumComponentQualifiers(DICTIONARY_DEBUG_GUID, i, rgchQualifier, &cchQualifier, NULL, NULL);
  202. if (componentState != ERROR_SUCCESS)
  203. break;
  204. // find the language ID
  205. // the string is formatted as 1033\xxxxxx
  206. // or 1042
  207. {
  208. TCHAR szLangId[cchMaxPathName];
  209. TCHAR *pSlash;
  210. lstrcpy(szLangId, rgchQualifier);
  211. pSlash = StrChr(szLangId, '\\');
  212. if (pSlash)
  213. *pSlash = 0;
  214. fContinue = (*pfn)(dwCookie, szLangId);
  215. }
  216. }
  217. #endif // DEBUG
  218. for(i=0; fContinue; i++)
  219. {
  220. cchQualifier = sizeof(rgchQualifier) / sizeof(rgchQualifier[0]);
  221. componentState = MsiEnumComponentQualifiers(DICTIONARY_GUID, i, rgchQualifier, &cchQualifier, NULL, NULL);
  222. if (componentState != ERROR_SUCCESS)
  223. break;
  224. // find the language ID
  225. // the string is formatted as 1033\xxxxxx
  226. // or 1042
  227. {
  228. TCHAR szLangId[cchMaxPathName];
  229. TCHAR *pSlash;
  230. lstrcpy(szLangId, rgchQualifier);
  231. pSlash = StrChr(szLangId, '\\');
  232. if (pSlash)
  233. *pSlash = 0;
  234. fContinue = (*pfn)(dwCookie, szLangId);
  235. }
  236. }
  237. return fContinue;
  238. }
  239. VOID EnumLanguages(DWORD_PTR dwCookie, LPFNENUMLANG pfn)
  240. {
  241. EnumNewSpellerLanguages(dwCookie, pfn);
  242. EnumOldSpellerLanguages(dwCookie, pfn);
  243. }
  244. BOOL EnumOffice9UserDictionaries(DWORD_PTR dwCookie, LPFNENUMUSERDICT pfn)
  245. {
  246. TCHAR rgchBuf[cchMaxPathName];
  247. HKEY hkey = NULL;
  248. FILETIME ft;
  249. DWORD iKey = 0;
  250. LONG lRet;
  251. TCHAR szValue[cchMaxPathName];
  252. DWORD cchValue;
  253. TCHAR szCustDict[cchMaxPathName];
  254. DWORD cchCustDict;
  255. BOOL fContinue = TRUE;
  256. BOOL fFoundUserDict = FALSE;
  257. TCHAR szOffice9Proof[cchMaxPathName]={0};
  258. // SOFTWARE\\Microsoft\\Shared Tools\\Proofing Tools\\Custom Dictionaries
  259. lstrcpy(rgchBuf, c_szRegSpellProfile);
  260. lstrcat(rgchBuf, c_szRegSpellKeyCustom);
  261. if(RegOpenKeyEx(HKEY_CURRENT_USER, rgchBuf, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS)
  262. {
  263. do
  264. {
  265. cchValue = sizeof(szValue) / sizeof(szValue[0]);
  266. cchCustDict = sizeof(szCustDict) / sizeof(szCustDict[0]);
  267. lRet = RegEnumValue(hkey,
  268. iKey++,
  269. szValue,
  270. &cchValue,
  271. NULL,
  272. NULL,
  273. (LPBYTE)szCustDict,
  274. &cchCustDict);
  275. if (lRet != ERROR_SUCCESS || lRet == ERROR_NO_MORE_ITEMS)
  276. break;
  277. fFoundUserDict = TRUE;
  278. // check to see if we have a path
  279. if (!(StrChr(szCustDict, ':') || StrChr(szCustDict, '\\')))
  280. {
  281. TCHAR szTemp[cchMaxPathName];
  282. if (!strlen(szOffice9Proof))
  283. {
  284. LPITEMIDLIST pidl;
  285. if (S_OK == SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, &pidl))
  286. SHGetPathFromIDList(pidl, szOffice9Proof);
  287. else
  288. {
  289. // if the Shell call fails (as it can on Win9x sometimes) let's get the info
  290. // from the registry
  291. HKEY hKeyShellFolders;
  292. ULONG cchAppData;
  293. if(RegOpenKeyEx(HKEY_CURRENT_USER, c_szRegShellFoldersKey, 0, KEY_QUERY_VALUE, &hKeyShellFolders) == ERROR_SUCCESS)
  294. {
  295. cchAppData = ARRAYSIZE(szOffice9Proof);
  296. RegQueryValueEx(hKeyShellFolders, c_szValueAppData, 0, NULL, (LPBYTE)szOffice9Proof, &cchAppData);
  297. RegCloseKey(hKeyShellFolders);
  298. }
  299. }
  300. // if this fails then we will try the current path
  301. }
  302. lstrcpy(szTemp, szOffice9Proof);
  303. lstrcat(szTemp, "\\");
  304. lstrcat(szTemp, c_szSpellOffice9ProofPath);
  305. lstrcat(szTemp, szCustDict);
  306. lstrcpy(szCustDict, szTemp);
  307. }
  308. fContinue = (*pfn)(dwCookie, szCustDict);
  309. } while (fContinue);
  310. }
  311. if (hkey)
  312. RegCloseKey(hkey);
  313. return fFoundUserDict;
  314. }
  315. BOOL EnumOfficeUserDictionaries(DWORD_PTR dwCookie, LPFNENUMUSERDICT pfn)
  316. {
  317. TCHAR rgchBuf[cchMaxPathName];
  318. HKEY hkey = NULL;
  319. FILETIME ft;
  320. DWORD iKey = 0;
  321. LONG lRet;
  322. TCHAR szValue[cchMaxPathName];
  323. DWORD cchValue;
  324. TCHAR szCustDict[cchMaxPathName];
  325. DWORD cchCustDict;
  326. BOOL fFoundUserDict = FALSE;
  327. BOOL fContinue = TRUE;
  328. // SOFTWARE\\Microsoft\\Shared Tools\\Proofing Tools\\Custom Dictionaries
  329. lstrcpy(rgchBuf, c_szRegSpellProfile);
  330. lstrcat(rgchBuf, c_szRegSpellKeyCustom);
  331. if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, rgchBuf, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS)
  332. {
  333. do
  334. {
  335. cchValue = sizeof(szValue) / sizeof(szValue[0]);
  336. cchCustDict = sizeof(szCustDict) / sizeof(szCustDict[0]);
  337. lRet = RegEnumValue(hkey,
  338. iKey++,
  339. szValue,
  340. &cchValue,
  341. NULL,
  342. NULL,
  343. (LPBYTE)szCustDict,
  344. &cchCustDict);
  345. if (lRet != ERROR_SUCCESS || lRet == ERROR_NO_MORE_ITEMS)
  346. break;
  347. fFoundUserDict = TRUE;
  348. fContinue = (*pfn)(dwCookie, szCustDict);
  349. } while (fContinue);
  350. }
  351. if (hkey)
  352. RegCloseKey(hkey);
  353. return fFoundUserDict;
  354. }
  355. VOID EnumUserDictionaries(DWORD_PTR dwCookie, LPFNENUMUSERDICT pfn)
  356. {
  357. // check for Office9 user dictionaries. If we find any
  358. // we bail.
  359. if (EnumOffice9UserDictionaries(dwCookie, pfn))
  360. return;
  361. EnumOfficeUserDictionaries(dwCookie, pfn);
  362. }
  363. /*
  364. * GetSpellingPaths
  365. *
  366. * Purpose:
  367. * Function to get Spelling DLL names.
  368. *
  369. * Arguments:
  370. * szKey c_szRegSpellKeyDef (with correct language)
  371. * szDefault c_szRegSpellEmpty
  372. * szReturnBuffer dll filename
  373. * szMdr dictionary filename
  374. * cchReturnBufer
  375. *
  376. * Returns:
  377. * DWORD
  378. */
  379. DWORD GetSpellingPaths(LPCTSTR szKey, LPTSTR szReturnBuffer, LPTSTR szMdr, UINT cchReturnBufer)
  380. {
  381. DWORD dwRet = 0;
  382. TCHAR rgchBuf[cchMaxPathName];
  383. DWORD dwType, cbData;
  384. HKEY hkey = NULL;
  385. LPTSTR szValue;
  386. szReturnBuffer[0] = 0;
  387. lstrcpy(rgchBuf, c_szRegSpellProfile);
  388. lstrcat(rgchBuf, szKey);
  389. if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, rgchBuf, 0, KEY_QUERY_VALUE, &hkey))
  390. goto err;
  391. cbData = cchReturnBufer * sizeof(TCHAR);
  392. szValue = (LPTSTR) (szMdr ? c_szRegSpellPath : c_szRegSpellPathDict);
  393. if (ERROR_SUCCESS != SHQueryValueEx(hkey, szValue, 0L, &dwType, (BYTE *) szReturnBuffer, &cbData))
  394. goto err;
  395. // Parse off the main dictionary filename
  396. if(szMdr)
  397. {
  398. szMdr[0] = 0;
  399. cbData = cchReturnBufer * sizeof(TCHAR);
  400. if (ERROR_SUCCESS != SHQueryValueEx(hkey, c_szRegSpellPathLex, 0L, &dwType, (BYTE *) szMdr, &cbData))
  401. goto err;
  402. }
  403. dwRet = cbData;
  404. err:
  405. if(hkey)
  406. RegCloseKey(hkey);
  407. return dwRet;
  408. }
  409. /*
  410. * SpellingDlgProc
  411. *
  412. * Purpose:
  413. * Dialog procedure for the Tools.Spelling dialog
  414. *
  415. * Arguments:
  416. * HWND Dialog procedure arguments.
  417. * UINT
  418. * WPARAM
  419. * LPARAM
  420. *
  421. * Returns:
  422. * BOOL TRUE if the message has been processed.
  423. */
  424. INT_PTR CALLBACK SpellingDlgProc(HWND hwndDlg, UINT wMsg, WPARAM wparam, LPARAM lparam)
  425. {
  426. CSpell* pSpell;
  427. HWND hwndEdited;
  428. HWND hwndSuggest;
  429. switch (wMsg)
  430. {
  431. case WM_INITDIALOG:
  432. SetWindowLongPtr(hwndDlg, DWLP_USER, lparam);
  433. pSpell = (CSpell*)lparam;
  434. pSpell->m_hwndDlg = hwndDlg;
  435. hwndEdited = GetDlgItem(hwndDlg, EDT_Spell_ChangeTo);
  436. hwndSuggest = GetDlgItem(hwndDlg, LBX_Spell_Suggest);
  437. pSpell->m_fEditWasEmpty = TRUE;
  438. SetDlgItemText(hwndDlg, TXT_Spell_Error, pSpell->m_szErrType);
  439. SetDlgItemText(hwndDlg, EDT_Spell_WrongWord, pSpell->m_szWrongWord);
  440. SetWindowText(hwndEdited, pSpell->m_szEdited);
  441. EnableWindow(GetDlgItem(hwndDlg, PSB_Spell_Options), FALSE);
  442. EnableWindow(GetDlgItem(hwndDlg, PSB_Spell_Add), (0 != pSpell->m_rgprflex[1]));
  443. pSpell->m_fRepeat = (pSpell->m_wsrb.sstat == sstatRepeatWord);
  444. EnableRepeatedWindows(pSpell, hwndDlg);
  445. if (!pSpell->m_fRepeat)
  446. pSpell->FillSuggestLbx();
  447. else
  448. ListBox_ResetContent(GetDlgItem(hwndDlg, LBX_Spell_Suggest));
  449. if (pSpell->m_fSuggestions && !pSpell->m_fNoneSuggested && !pSpell->m_fRepeat)
  450. {
  451. EnableWindow(hwndSuggest, TRUE);
  452. ListBox_SetCurSel(hwndSuggest, 0);
  453. UpdateEditedFromSuggest(hwndDlg, hwndEdited, hwndSuggest);
  454. EnableWindow(GetDlgItem(hwndDlg, TXT_Spell_Suggest), TRUE);
  455. // Set initial default button to "Change"
  456. SendMessage(hwndDlg, DM_SETDEFID, PSB_Spell_Change, 0L);
  457. Button_SetStyle(GetDlgItem(hwndDlg, PSB_Spell_Change), BS_DEFPUSHBUTTON, TRUE);
  458. EnableWindow(GetDlgItem(hwndDlg, PSB_Spell_Suggest), FALSE);
  459. }
  460. else
  461. {
  462. Edit_SetSel(hwndEdited, 0, 32767); // select the whole thing
  463. EnableWindow(hwndSuggest, FALSE);
  464. EnableWindow(GetDlgItem(hwndDlg, TXT_Spell_Suggest), FALSE);
  465. // Set initial default button to "Ignore"
  466. SendMessage(hwndDlg, DM_SETDEFID, PSB_Spell_Ignore, 0L);
  467. Button_SetStyle(GetDlgItem(hwndDlg, PSB_Spell_Ignore), BS_DEFPUSHBUTTON, TRUE);
  468. EnableWindow(GetDlgItem(hwndDlg, PSB_Spell_Suggest), !pSpell->m_fSuggestions && !pSpell->m_fRepeat);
  469. }
  470. AthFixDialogFonts(hwndDlg);
  471. SetFocus(hwndEdited);
  472. break;
  473. case WM_DESTROY:
  474. break;
  475. case WM_COMMAND:
  476. return SpellingOnCommand(hwndDlg, wMsg, wparam, lparam);
  477. }
  478. return FALSE;
  479. }
  480. void EnableRepeatedWindows(CSpell* pSpell, HWND hwndDlg)
  481. {
  482. INT ids;
  483. EnableWindow(GetDlgItem(hwndDlg, PSB_Spell_Add), (!pSpell->m_fRepeat && (0 != pSpell->m_rgprflex[1])));
  484. EnableWindow(GetDlgItem(hwndDlg, PSB_Spell_IgnoreAll), !pSpell->m_fRepeat);
  485. EnableWindow(GetDlgItem(hwndDlg, PSB_Spell_ChangeAll), !pSpell->m_fRepeat);
  486. if (pSpell->m_fRepeat)
  487. {
  488. SetWindowText(GetDlgItem(hwndDlg, EDT_Spell_ChangeTo), "");
  489. *pSpell->m_szEdited = 0;
  490. }
  491. }
  492. BOOL SpellingOnCommand(HWND hwndDlg, UINT wMsg, WPARAM wparam, LPARAM lparam)
  493. {
  494. CSpell* pSpell;
  495. BOOL fChange;
  496. BOOL fAlwaysSuggest;
  497. BOOL fUndoing = FALSE;
  498. HRESULT hr = 0;
  499. pSpell = (CSpell*) GetWindowLongPtr(hwndDlg, DWLP_USER);
  500. Assert(pSpell);
  501. // Update our replacement word? Only do this when a button is clicked
  502. // or a double-click from the suggest listbox, and the word has been modified.
  503. if ((GET_WM_COMMAND_CMD(wparam, lparam) == BN_CLICKED ||
  504. GET_WM_COMMAND_CMD(wparam, lparam) == LBN_DBLCLK) &&
  505. Edit_GetModify(GetDlgItem(hwndDlg, EDT_Spell_ChangeTo)))
  506. {
  507. HWND hwndEditChangeTo;
  508. hwndEditChangeTo = GetDlgItem(pSpell->m_hwndDlg, EDT_Spell_ChangeTo);
  509. Edit_SetModify(hwndEditChangeTo, FALSE);
  510. pSpell->m_fSuggestions = FALSE;
  511. GetWindowText(hwndEditChangeTo, pSpell->m_szEdited, 512);
  512. }
  513. switch(GET_WM_COMMAND_ID(wparam, lparam))
  514. {
  515. case LBX_Spell_Suggest:
  516. if (GET_WM_COMMAND_CMD(wparam, lparam) == LBN_SELCHANGE)
  517. {
  518. UpdateEditedFromSuggest(hwndDlg, GetDlgItem(hwndDlg, EDT_Spell_ChangeTo),
  519. GetDlgItem(hwndDlg, LBX_Spell_Suggest));
  520. return TRUE;
  521. }
  522. else if (GET_WM_COMMAND_CMD(wparam, lparam) == LBN_DBLCLK)
  523. {
  524. goto ChangeIt;
  525. }
  526. else
  527. {
  528. return FALSE;
  529. }
  530. case EDT_Spell_ChangeTo:
  531. if (GET_WM_COMMAND_CMD(wparam, lparam) == EN_CHANGE)
  532. {
  533. INT idClearDefault;
  534. INT idSetDefault;
  535. BOOL fEditModified;
  536. // We get EN_CHANGE notifications for both a SetWindowText() and user modifications.
  537. // Look at the dirty flag (only set on user mods) and the state of the suggestions
  538. // selection to see which button should get the default style.
  539. fEditModified = Edit_GetModify(GET_WM_COMMAND_HWND(wparam, lparam));
  540. if (fEditModified || pSpell->m_fSuggestions && !pSpell->m_fNoneSuggested)
  541. {
  542. idClearDefault = PSB_Spell_Ignore;
  543. idSetDefault = PSB_Spell_Change;
  544. }
  545. else
  546. {
  547. idClearDefault = PSB_Spell_Change;
  548. idSetDefault = PSB_Spell_Ignore;
  549. }
  550. // Enable/disable Suggest button
  551. EnableWindow(GetDlgItem(hwndDlg, PSB_Spell_Suggest), fEditModified);
  552. // Set default button
  553. Button_SetStyle(GetDlgItem(hwndDlg, idClearDefault), BS_PUSHBUTTON, TRUE);
  554. SendMessage(hwndDlg, DM_SETDEFID, idSetDefault, 0L);
  555. Button_SetStyle(GetDlgItem(hwndDlg, idSetDefault), BS_DEFPUSHBUTTON, TRUE);
  556. // "Change" button title
  557. if (GetWindowTextLength(GET_WM_COMMAND_HWND(wparam, lparam)) && pSpell->m_fEditWasEmpty)
  558. {
  559. pSpell->m_fEditWasEmpty = FALSE;
  560. LoadString(g_hLocRes, idsSpellChange, pSpell->m_szTempBuffer,
  561. sizeof(pSpell->m_szTempBuffer)/sizeof(TCHAR));
  562. SetDlgItemText(hwndDlg, PSB_Spell_Change, pSpell->m_szTempBuffer);
  563. LoadString(g_hLocRes, idsSpellChangeAll, pSpell->m_szTempBuffer,
  564. sizeof(pSpell->m_szTempBuffer)/sizeof(TCHAR));
  565. SetDlgItemText(hwndDlg, PSB_Spell_ChangeAll, pSpell->m_szTempBuffer);
  566. }
  567. else if (GetWindowTextLength(GET_WM_COMMAND_HWND(wparam, lparam)) == 0)
  568. {
  569. pSpell->m_fEditWasEmpty = TRUE;
  570. LoadString(g_hLocRes, idsSpellDelete, pSpell->m_szTempBuffer,
  571. sizeof(pSpell->m_szTempBuffer)/sizeof(TCHAR));
  572. SetDlgItemText(hwndDlg, PSB_Spell_Change, pSpell->m_szTempBuffer);
  573. LoadString(g_hLocRes, idsSpellDeleteAll, pSpell->m_szTempBuffer,
  574. sizeof(pSpell->m_szTempBuffer)/sizeof(TCHAR));
  575. SetDlgItemText(hwndDlg, PSB_Spell_ChangeAll, pSpell->m_szTempBuffer);
  576. }
  577. }
  578. return TRUE;
  579. case PSB_Spell_IgnoreAll:
  580. {
  581. PROOFLEX lexIgnoreAll;
  582. lexIgnoreAll = pSpell->m_pfnSpellerBuiltInUdr(pSpell->m_pid, lxtIgnoreAlways);
  583. if (0 != lexIgnoreAll)
  584. {
  585. RemoveTrailingSpace(pSpell->m_szWrongWord);
  586. pSpell->AddToUdrA(pSpell->m_szWrongWord, lexIgnoreAll);
  587. pSpell->m_fCanUndo = FALSE;
  588. }
  589. }
  590. // [email protected] - remove the annoying dialog and just break out of here - 34229
  591. break;
  592. case PSB_Spell_Ignore:
  593. // Due to limitations with the spelling engine and our single undo level,
  594. // we can't allow undo's of Ignore if the error was a Repeated Word.
  595. if (pSpell->m_wsrb.sstat == sstatRepeatWord)
  596. pSpell->m_fCanUndo = FALSE;
  597. else
  598. pSpell->SpellSaveUndo(PSB_Spell_Ignore);
  599. // [email protected] - remove the annoying dialog and just break out of here - 34229
  600. break;
  601. case PSB_Spell_ChangeAll:
  602. if (pSpell->FVerifyThisText(pSpell->m_szEdited, FALSE))
  603. {
  604. pSpell->m_fCanUndo = FALSE;
  605. hr = pSpell->HrReplaceErrorText(TRUE, TRUE);
  606. break;
  607. }
  608. else
  609. {
  610. return TRUE;
  611. }
  612. case PSB_Spell_Change:
  613. ChangeIt:
  614. if (pSpell->FVerifyThisText(pSpell->m_szEdited, FALSE))
  615. {
  616. pSpell->m_fUndoChange = TRUE;
  617. pSpell->SpellSaveUndo(PSB_Spell_Change);
  618. hr = pSpell->HrReplaceErrorText(FALSE, TRUE);
  619. break;
  620. }
  621. else
  622. {
  623. return TRUE;
  624. }
  625. case PSB_Spell_Add:
  626. Assert(pSpell->m_rgprflex[1]);
  627. pSpell->m_fCanUndo = FALSE;
  628. fChange = FALSE;
  629. RemoveTrailingSpace(pSpell->m_szWrongWord);
  630. // [email protected] - removed the FVerifyThisText that was here - no need
  631. // to FVerifyThisText if the user is asking us to Add this word - fixes 55587
  632. pSpell->AddToUdrA(pSpell->m_szWrongWord, pSpell->m_rgprflex[1]);
  633. if (fChange)
  634. hr = pSpell->HrReplaceErrorText(FALSE, TRUE);
  635. break;
  636. case PSB_Spell_UndoLast:
  637. pSpell->SpellDoUndo();
  638. fUndoing = TRUE;
  639. break;
  640. case PSB_Spell_Suggest:
  641. hr = pSpell->HrSpellSuggest();
  642. if (FAILED(hr))
  643. goto bail;
  644. goto loadcache;
  645. case IDCANCEL:
  646. pSpell->m_fShowDoneMsg = FALSE;
  647. EndDialog(hwndDlg, IDCANCEL);
  648. return TRUE;
  649. default:
  650. return FALSE;
  651. }
  652. // If no current error, then proceed with checking the rest
  653. if (SUCCEEDED(hr))
  654. {
  655. // Change "Cancel" button to "Close"
  656. LoadString(g_hLocRes, idsSpellClose, pSpell->m_szTempBuffer,
  657. sizeof(pSpell->m_szTempBuffer)/sizeof(TCHAR));
  658. SetDlgItemText(hwndDlg, IDCANCEL, pSpell->m_szTempBuffer);
  659. pSpell->m_wsrb.sstat = sstatNoErrors;
  660. hr = pSpell->HrFindErrors();
  661. if(FAILED(hr))
  662. goto bail;
  663. if(pSpell->m_wsrb.sstat==sstatNoErrors)
  664. {
  665. EndDialog(hwndDlg, GET_WM_COMMAND_ID(wparam, lparam));
  666. return TRUE;
  667. }
  668. }
  669. bail:
  670. if(FAILED(hr))
  671. {
  672. EndDialog(hwndDlg, IDCANCEL);
  673. return TRUE;
  674. }
  675. SetDlgItemText(hwndDlg, EDT_Spell_WrongWord, pSpell->m_szWrongWord);
  676. SetDlgItemText(hwndDlg, TXT_Spell_Error, pSpell->m_szErrType);
  677. SetDlgItemText(hwndDlg, EDT_Spell_ChangeTo, pSpell->m_szEdited);
  678. EnableWindow(GetDlgItem(hwndDlg, PSB_Spell_UndoLast), pSpell->m_fCanUndo);
  679. EnableWindow(GetDlgItem(hwndDlg, PSB_Spell_Add), (0 != pSpell->m_rgprflex[1]));
  680. pSpell->m_fRepeat = (pSpell->m_wsrb.sstat == sstatRepeatWord);
  681. EnableRepeatedWindows(pSpell, hwndDlg);
  682. loadcache:
  683. if (!pSpell->m_fRepeat)
  684. pSpell->FillSuggestLbx();
  685. else
  686. ListBox_ResetContent(GetDlgItem(hwndDlg, LBX_Spell_Suggest));
  687. EnableWindow(GetDlgItem(hwndDlg, PSB_Spell_Suggest), !pSpell->m_fSuggestions && !pSpell->m_fRepeat);
  688. if (pSpell->m_fSuggestions && !pSpell->m_fNoneSuggested && !pSpell->m_fRepeat)
  689. {
  690. EnableWindow(GetDlgItem(hwndDlg, TXT_Spell_Suggest), TRUE);
  691. EnableWindow(GetDlgItem(hwndDlg, LBX_Spell_Suggest), TRUE);
  692. ListBox_SetCurSel(GetDlgItem(hwndDlg, LBX_Spell_Suggest), 0);
  693. UpdateEditedFromSuggest(hwndDlg, GetDlgItem(hwndDlg, EDT_Spell_ChangeTo),
  694. GetDlgItem(hwndDlg, LBX_Spell_Suggest));
  695. }
  696. else
  697. {
  698. EnableWindow(GetDlgItem(hwndDlg, TXT_Spell_Suggest), FALSE);
  699. EnableWindow(GetDlgItem(hwndDlg, LBX_Spell_Suggest), FALSE);
  700. }
  701. SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, EDT_Spell_ChangeTo), MAKELONG(TRUE,0));
  702. return TRUE;
  703. }
  704. void RemoveTrailingSpace(LPTSTR lpszWord)
  705. {
  706. LPTSTR lpsz;
  707. lpsz = StrChrI(lpszWord, ' ');
  708. if(lpsz)
  709. *lpsz = 0;
  710. }
  711. BOOL GetNewSpellerEngine(LANGID lgid, TCHAR *rgchEngine, DWORD cchEngine, TCHAR *rgchLex, DWORD cchLex, BOOL bTestAvail)
  712. {
  713. DWORD er;
  714. LPCSTR rgpszDictionaryTypes[] = {"Normal", "Consise", "Complete"};
  715. int cDictTypes = sizeof(rgpszDictionaryTypes) / sizeof(LPCSTR);
  716. int i;
  717. TCHAR rgchQual[MAX_PATH];
  718. bool fFound = FALSE;
  719. DWORD cch;
  720. INSTALLUILEVEL iuilOriginal;
  721. if (rgchEngine == NULL || rgchLex == NULL)
  722. return FALSE;
  723. *rgchEngine = 0;
  724. *rgchLex = 0;
  725. wsprintf(rgchQual, "%d\\Normal", lgid);
  726. cch = cchEngine;
  727. if (bTestAvail)
  728. {
  729. // Explicitly Turn off internal installer UI
  730. // Eg: A feature is set to "run from CD," and CD is not present - fail silently
  731. // OE Bug 74697
  732. iuilOriginal = MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
  733. }
  734. #ifdef DEBUG
  735. er = MsiProvideQualifiedComponent(SPELLER_DEBUG_GUID, rgchQual, (bTestAvail ? INSTALLMODE_EXISTING : INSTALLMODE_DEFAULT), rgchEngine, &cch);
  736. if ((er != ERROR_SUCCESS) && (er != ERROR_FILE_NOT_FOUND))
  737. {
  738. cch = cchEngine;
  739. er = MsiProvideQualifiedComponent(SPELLER_GUID, rgchQual, (bTestAvail ? INSTALLMODE_EXISTING : INSTALLMODE_DEFAULT), rgchEngine, &cch);
  740. }
  741. #else
  742. er = MsiProvideQualifiedComponent(SPELLER_GUID, rgchQual, (bTestAvail ? INSTALLMODE_EXISTING : INSTALLMODE_DEFAULT), rgchEngine, &cch);
  743. #endif
  744. if ((er != ERROR_SUCCESS) && (er != ERROR_FILE_NOT_FOUND))
  745. {
  746. fFound = FALSE;
  747. goto errorExit;
  748. }
  749. // Hebrew has main lex in new speller
  750. for (i = 0; i < cDictTypes; i++)
  751. {
  752. wsprintf(rgchQual, "%d\\%s", lgid, rgpszDictionaryTypes[i]);
  753. cch = cchLex;
  754. #ifdef DEBUG
  755. er = MsiProvideQualifiedComponent(DICTIONARY_DEBUG_GUID, rgchQual, (bTestAvail ? INSTALLMODE_EXISTING : INSTALLMODE_DEFAULT), rgchLex, &cch);
  756. if ((er != ERROR_SUCCESS) && (er != ERROR_FILE_NOT_FOUND))
  757. {
  758. cch = cchLex;
  759. er = MsiProvideQualifiedComponent(DICTIONARY_GUID, rgchQual, (bTestAvail ? INSTALLMODE_EXISTING : INSTALLMODE_DEFAULT), rgchLex, &cch);
  760. }
  761. #else // DEBUG
  762. er = MsiProvideQualifiedComponent(DICTIONARY_GUID, rgchQual, (bTestAvail ? INSTALLMODE_EXISTING : INSTALLMODE_DEFAULT), rgchLex, &cch);
  763. #endif // DEBUG
  764. if ((er == ERROR_SUCCESS) || (er == ERROR_FILE_NOT_FOUND))
  765. {
  766. fFound = TRUE;
  767. break;
  768. }
  769. }
  770. errorExit:
  771. if (bTestAvail)
  772. {
  773. // Restore original UI Level
  774. MsiSetInternalUI(iuilOriginal, NULL);
  775. }
  776. return fFound;
  777. }
  778. BOOL FIsNewSpellerInstaller(IOleCommandTarget* pParentCmdTarget)
  779. {
  780. LANGID langid;
  781. TCHAR rgchEngine[MAX_PATH];
  782. int cchEngine = sizeof(rgchEngine) / sizeof(rgchEngine[0]);
  783. TCHAR rgchLex[MAX_PATH];
  784. int cchLex = sizeof(rgchLex) / sizeof(rgchLex[0]);
  785. // first try to load dictionaries for various languages
  786. langid = WGetLangID(pParentCmdTarget);
  787. if (!GetNewSpellerEngine(langid, rgchEngine, cchEngine, rgchLex, cchLex, TRUE))
  788. {
  789. langid = GetSystemDefaultLangID();
  790. if (!GetNewSpellerEngine(langid, rgchEngine, cchEngine, rgchLex, cchLex, TRUE))
  791. {
  792. langid = 1033; // bloody cultural imperialists.
  793. if (!GetNewSpellerEngine(langid, rgchEngine, cchEngine, rgchLex, cchLex, TRUE))
  794. return FALSE;
  795. }
  796. }
  797. return TRUE;
  798. }
  799. // [email protected] - copied from "old" spell code - 32675
  800. /*
  801. * FIsSpellingInstalled
  802. *
  803. * Purpose:
  804. * Is the spelling stuff installed
  805. *
  806. * Arguments:
  807. * none
  808. *
  809. * Returns:
  810. * BOOL Returns TRUE if spelling is installed, else FALSE.
  811. */
  812. BOOL FIsSpellingInstalled(IOleCommandTarget* pParentCmdTarget)
  813. {
  814. TCHAR rgchBufDigit[10];
  815. if (GetLangID(pParentCmdTarget, rgchBufDigit, sizeof(rgchBufDigit)/sizeof(TCHAR)) && !FBadSpellChecker(rgchBufDigit))
  816. return true;
  817. if (FIsNewSpellerInstaller(pParentCmdTarget))
  818. return true;
  819. return false;
  820. }
  821. // Does a quick check to see if spelling is available; caches result.
  822. BOOL FCheckSpellAvail(IOleCommandTarget* pParentCmdTarget)
  823. {
  824. // [email protected] - copied from "old" spell code - 32675
  825. static int fSpellAvailable = -1;
  826. if (fSpellAvailable < 0)
  827. fSpellAvailable = (FIsSpellingInstalled(pParentCmdTarget) ? 1 : 0);
  828. return (fSpellAvailable > 0);
  829. }
  830. HRESULT CSpell::HrSpellReset()
  831. {
  832. m_fSpellContinue = FALSE;
  833. return NOERROR;
  834. }
  835. /*
  836. * UlNoteCmdToolsSpelling
  837. *
  838. * Purpose:
  839. * An interface layer between the note and core spelling code
  840. *
  841. * Arguments:
  842. * HWND Owning window handle, main window
  843. * HWND Subject line window, checked first, actually.
  844. * BOOL Suppress done message (used when spell-check on send)
  845. *
  846. * Returns:
  847. * ULONG Returns 0 if spelling check was completed, else returns non-zero
  848. * if an error occurred or user cancelled the spell check.
  849. */
  850. HRESULT CSpell::HrSpellChecking(IHTMLTxtRange *pRangeIgnore, HWND hwndMain, BOOL fSuppressDoneMsg)
  851. {
  852. HRESULT hr = NOERROR;
  853. hr = HrSpellReset();
  854. if (FAILED(hr))
  855. goto End;
  856. hr = HrInitRanges(pRangeIgnore, hwndMain, fSuppressDoneMsg);
  857. if(FAILED(hr))
  858. goto End;
  859. hr = HrFindErrors();
  860. if(FAILED(hr))
  861. goto End;
  862. if(m_wsrb.sstat==sstatNoErrors && m_fShowDoneMsg)
  863. AthMessageBoxW(m_hwndNote, MAKEINTRESOURCEW(idsSpellCaption), MAKEINTRESOURCEW(idsSpellMsgDone), NULL, MB_OK | MB_ICONINFORMATION);
  864. End:
  865. DeInitRanges();
  866. return hr;
  867. }
  868. #ifdef BACKGROUNDSPELL
  869. HRESULT CSpell::HrBkgrndSpellTimer()
  870. {
  871. HRESULT hr=NOERROR;
  872. LONG cSpellWords = 0;
  873. IHTMLTxtRange *pTxtRange=0;
  874. LONG cb;
  875. VARIANT_BOOL fSuccess;
  876. if (m_Stack.fEmpty())
  877. goto error;
  878. while (!(m_Stack.fEmpty()) && cSpellWords <= MAX_SPELLWORDS)
  879. {
  880. m_Stack.HrGetRange(&pTxtRange);
  881. if (pTxtRange)
  882. {
  883. while(cSpellWords <= MAX_SPELLWORDS)
  884. {
  885. pTxtRange->collapse(VARIANT_TRUE);
  886. if (SUCCEEDED(pTxtRange->expand((BSTR)c_bstr_Word, &fSuccess)) && fSuccess==VARIANT_TRUE)
  887. {
  888. HrBkgrndSpellCheck(pTxtRange);
  889. cSpellWords++;
  890. }
  891. else
  892. {
  893. m_Stack.pop();
  894. SafeRelease(pTxtRange);
  895. break;
  896. }
  897. cb=0;
  898. if (FAILED(pTxtRange->moveStart((BSTR)c_bstr_Word, 1, &cb)) || cb!=1)
  899. {
  900. m_Stack.pop();
  901. SafeRelease(pTxtRange);
  902. break;
  903. }
  904. }
  905. }
  906. }
  907. error:
  908. SafeRelease(pTxtRange);
  909. return hr;
  910. }
  911. #endif // BACKGROUNDSPELL
  912. #ifdef BACKGROUNDSPELL
  913. HRESULT CSpell::HrBkgrndSpellCheck(IHTMLTxtRange *pTxtRange)
  914. {
  915. HRESULT hr = NOERROR;
  916. LPSTR pszText = 0;
  917. VARIANT_BOOL fSuccess;
  918. hr = pTxtRange->expand((BSTR)c_bstr_Word, &fSuccess);
  919. if(FAILED(hr))
  920. goto error;
  921. hr = HrGetText(pTxtRange, &pszText);
  922. if(FAILED(hr))
  923. goto error;
  924. if (hr == HR_S_SPELLCONTINUE)
  925. goto error;
  926. StripNBSPs(pszText);
  927. // ignore words with wildcards
  928. if (StrChr(pszText, '*'))
  929. goto error;
  930. RemoveTrailingSpace(pszText);
  931. hr = HrCheckWord(pszText);
  932. if(FAILED(hr))
  933. goto error;
  934. if(m_wsrb.sstat!=sstatNoErrors && m_wsrb.sstat!=sstatRepeatWord) //found an error.
  935. {
  936. // FIgnore should take pTxtRange as the parameter.
  937. if(FIgnore(pTxtRange))
  938. {
  939. m_wsrb.sstat = sstatNoErrors;
  940. goto error;
  941. }
  942. if (HrHasSquiggle(pTxtRange)==S_OK)
  943. {
  944. DebugTrace("Spell: Bad word %s\n", pszText);
  945. goto error;
  946. }
  947. //put red squiggle
  948. HrSetSquiggle(pTxtRange);
  949. }
  950. else //if the wrong word is corrected, delete <U> tag.
  951. {
  952. if (HrHasSquiggle(pTxtRange)==S_OK)
  953. HrDeleteSquiggle(pTxtRange);
  954. }
  955. error:
  956. SafeMemFree(pszText);
  957. return hr;
  958. }
  959. #endif // BACKGROUNDSPELL
  960. #ifdef BACKGROUNDSPELL
  961. //const static CHAR c_szSquiggleFmt[] = "<U style='color:red'>%s</U>";
  962. const static CHAR c_szSquiggleFmt[] = "<SPAN class=badspelling STYLE='text-decoration:underline;color:red'>%s</SPAN>";
  963. HRESULT CSpell::HrSetSquiggle(IHTMLTxtRange *pTxtRange)
  964. {
  965. CHAR szBuf[MAX_PATH]={0};
  966. BSTR bstr=0;
  967. HRESULT hr=NOERROR;
  968. LPSTR pszText=0;
  969. INT nSpaces=0;
  970. int i;
  971. hr = HrGetText(pTxtRange, &pszText);
  972. if(FAILED(hr))
  973. goto error;
  974. if (hr == HR_S_SPELLCONTINUE)
  975. goto error;
  976. hr = HrGetSpaces(pszText, &nSpaces);
  977. if(FAILED(hr))
  978. goto error;
  979. RemoveTrailingSpace(pszText);
  980. wsprintf(szBuf, c_szSquiggleFmt, pszText);
  981. for(i=0; i<(nSpaces-1); i++)
  982. lstrcat(szBuf, "&nbsp");
  983. if (nSpaces>0)
  984. lstrcat(szBuf, " ");
  985. HrLPSZToBSTR(szBuf, &bstr);
  986. hr = pTxtRange->pasteHTML(bstr);
  987. if(FAILED(hr))
  988. goto error;
  989. error:
  990. SafeSysFreeString(bstr);
  991. SafeMemFree(pszText);
  992. return hr;
  993. }
  994. #endif // BACKGROUNDSPELL
  995. #ifdef BACKGROUNDSPELL
  996. HRESULT CSpell::HrDeleteSquiggle(IHTMLTxtRange *pTxtRange)
  997. {
  998. CHAR szBuf[MAX_PATH]={0};
  999. BSTR bstr=0;
  1000. HRESULT hr=NOERROR;
  1001. LPSTR pszText=0;
  1002. INT nSpaces=0;
  1003. int i;
  1004. hr = HrGetText(pTxtRange, &pszText);
  1005. if(FAILED(hr))
  1006. goto error;
  1007. if (hr == HR_S_SPELLCONTINUE)
  1008. goto error;
  1009. hr = HrGetSpaces(pszText, &nSpaces);
  1010. if(FAILED(hr))
  1011. goto error;
  1012. lstrcpy(szBuf, pszText);
  1013. for(i=0; i<(nSpaces-1); i++)
  1014. lstrcat(szBuf, "&nbsp");
  1015. if (nSpaces>0)
  1016. lstrcat(szBuf, " ");
  1017. HrLPSZToBSTR(szBuf, &bstr);
  1018. hr = pTxtRange->pasteHTML(bstr);
  1019. if(FAILED(hr))
  1020. goto error;
  1021. error:
  1022. SafeSysFreeString(bstr);
  1023. SafeMemFree(pszText);
  1024. return hr;
  1025. }
  1026. #endif // BACKGROUNDSPELL
  1027. HRESULT CSpell::HrGetSpaces(LPSTR pszText, INT* pnSpaces)
  1028. {
  1029. LPSTR p;
  1030. *pnSpaces = 0;
  1031. p = StrChrI(pszText, ' ');
  1032. if(p)
  1033. {
  1034. *pnSpaces = (INT) (&pszText[lstrlen(pszText)] - p);
  1035. Assert(*pnSpaces>=0);
  1036. }
  1037. return NOERROR;
  1038. }
  1039. HRESULT CSpell::HrInsertMenu(HMENU hmenu, IHTMLTxtRange *pTxtRange)
  1040. {
  1041. LPSTR pch=0;
  1042. LPSTR pszText=0;
  1043. INT index=0;
  1044. HRESULT hr;
  1045. VARIANT_BOOL fSuccess;
  1046. hr = pTxtRange->expand((BSTR)c_bstr_Word, &fSuccess);
  1047. if(FAILED(hr))
  1048. goto error;
  1049. hr = HrGetText(pTxtRange, &pszText);
  1050. if(FAILED(hr))
  1051. goto error;
  1052. if (pszText==NULL || *pszText=='\0')
  1053. {
  1054. hr = E_FAIL;
  1055. goto error;
  1056. }
  1057. strcpy(m_szEdited, pszText);
  1058. HrSpellSuggest();
  1059. pch = m_szSuggest;
  1060. if (*pch == '\0')
  1061. {
  1062. LoadString(g_hLocRes, idsSpellNoSuggestions, m_szTempBuffer,
  1063. sizeof(m_szTempBuffer)/sizeof(TCHAR));
  1064. InsertMenu(hmenu, (UINT)0, MF_BYPOSITION|MF_STRING, idmSuggest0, m_szTempBuffer);
  1065. EnableMenuItem(hmenu, idmSuggest0, MF_BYCOMMAND|MF_GRAYED);
  1066. //ListBox_AddString(hwndLbx, m_szTempBuffer);
  1067. }
  1068. else
  1069. {
  1070. while(*pch != '\0' && index<5)
  1071. {
  1072. InsertMenu(hmenu, (UINT)index, MF_BYPOSITION|MF_STRING, idmSuggest0 + index, pch);
  1073. index++;
  1074. //ListBox_AddString(hwndLbx, pch);
  1075. while(*pch != '\0')
  1076. pch++;
  1077. pch++;
  1078. }
  1079. }
  1080. error:
  1081. SafeMemFree(pszText);
  1082. return hr;
  1083. }
  1084. const static TCHAR c_szFmt[] = "%s%s";
  1085. HRESULT CSpell::HrReplaceBySuggest(IHTMLTxtRange *pTxtRange, INT index)
  1086. {
  1087. CHAR szBuf[MAX_PATH] = {0};
  1088. BSTR bstr=0;
  1089. BSTR bstrPut=0;
  1090. LPSTR pch=0;
  1091. INT i=0;
  1092. HRESULT hr;
  1093. pch = m_szSuggest;
  1094. while(*pch != '\0' && i<5)
  1095. {
  1096. if (index == i)
  1097. {
  1098. strcpy(szBuf, pch);
  1099. if (SUCCEEDED(pTxtRange->get_text(&bstr)) && bstr)
  1100. {
  1101. LPSTR pszText = 0;
  1102. if (SUCCEEDED(HrBSTRToLPSZ(CP_ACP, bstr, &pszText)) && pszText)
  1103. {
  1104. LPSTR psz;
  1105. INT nSpaces=0;
  1106. psz = StrChrI(pszText, ' ');
  1107. if(psz)
  1108. {
  1109. nSpaces = (INT) (&pszText[lstrlen(pszText)] - psz);
  1110. Assert(nSpaces>=0);
  1111. for(int i=0; i<(nSpaces-1); i++)
  1112. lstrcat(szBuf, "&nbsp;");
  1113. if (nSpaces>0)
  1114. lstrcat(szBuf, " ");
  1115. }
  1116. hr = HrLPSZToBSTR(szBuf, &bstrPut);
  1117. SafeMemFree(pszText);
  1118. }
  1119. SafeSysFreeString(bstr);
  1120. }
  1121. if (bstrPut)
  1122. {
  1123. pTxtRange->pasteHTML(bstrPut);
  1124. SafeSysFreeString(bstrPut);
  1125. }
  1126. break;
  1127. }
  1128. i++;
  1129. while(*pch != '\0')
  1130. pch++;
  1131. pch++;
  1132. }
  1133. return NOERROR;
  1134. }
  1135. HRESULT CSpell::HrFindErrors()
  1136. {
  1137. HRESULT hr = NOERROR;
  1138. if(m_State == SEL)
  1139. {
  1140. hr = HrCheckRange(m_pRangeSelExpand);
  1141. // if hr==HR_S_ABORT, quit so as to pass control to dialog procedure which calls HrFindErrors.
  1142. if(TESTHR(hr))
  1143. goto error;
  1144. if(AthMessageBoxW(m_hwndDlg ? m_hwndDlg : m_hwndNote,
  1145. MAKEINTRESOURCEW(idsSpellCaption),
  1146. MAKEINTRESOURCEW(idsSpellMsgContinue),
  1147. NULL,
  1148. MB_YESNO | MB_ICONEXCLAMATION ) != IDYES)
  1149. {
  1150. m_fShowDoneMsg = FALSE;
  1151. goto error;
  1152. }
  1153. CleanupState();
  1154. }
  1155. if(m_State == SELENDDOCEND)
  1156. {
  1157. DumpRange(m_pRangeSelEndDocEnd);
  1158. m_fIgnoreScope = TRUE;
  1159. hr = HrCheckRange(m_pRangeSelEndDocEnd);
  1160. m_fIgnoreScope = FALSE;
  1161. if(TESTHR(hr))
  1162. goto error;
  1163. CleanupState();
  1164. hr = HrSpellReset();
  1165. if (FAILED(hr))
  1166. goto error;
  1167. }
  1168. if(m_State == DOCSTARTSELSTART)
  1169. {
  1170. hr = HrCheckRange(m_pRangeDocStartSelStart);
  1171. if(TESTHR(hr))
  1172. goto error;
  1173. CleanupState();
  1174. }
  1175. error:
  1176. // save the hr so as to know if something went wrong when dialog procedure calls HrFindErrors.
  1177. m_hr = hr;
  1178. return hr;
  1179. }
  1180. VOID CSpell::CleanupState()
  1181. {
  1182. m_State++;
  1183. SafeRelease(m_pRangeChecking);
  1184. }
  1185. HRESULT CSpell::HrCheckRange(IHTMLTxtRange* pRange)
  1186. {
  1187. HRESULT hr = NOERROR;
  1188. INT_PTR nCode;
  1189. LPSTR pszText = 0;
  1190. VARIANT_BOOL fSuccess;
  1191. if(m_pRangeChecking == NULL)
  1192. {
  1193. hr = pRange->duplicate(&m_pRangeChecking);
  1194. if(FAILED(hr))
  1195. goto error;
  1196. hr = m_pRangeChecking->collapse(VARIANT_TRUE);
  1197. if(FAILED(hr))
  1198. goto error;
  1199. }
  1200. while(TRUE)
  1201. {
  1202. SafeMemFree(pszText);
  1203. hr = HrGetNextWordRange(m_pRangeChecking);
  1204. if(FAILED(hr))
  1205. goto error;
  1206. if (hr == HR_S_SPELLBREAK)
  1207. break;
  1208. if (hr == HR_S_SPELLCONTINUE)
  1209. continue;
  1210. // Do we really need it?
  1211. if (!m_fIgnoreScope)
  1212. {
  1213. hr = pRange->inRange(m_pRangeChecking, &fSuccess);
  1214. if(FAILED(hr))
  1215. goto error;
  1216. if(fSuccess != VARIANT_TRUE)
  1217. break;
  1218. }
  1219. // check if we are on the original text of a reply/forward message.
  1220. if(m_pRangeIgnore)
  1221. {
  1222. fSuccess = VARIANT_FALSE;
  1223. m_pRangeIgnore->inRange(m_pRangeChecking, &fSuccess);
  1224. // normally don't spellcheck words in m_pRangeIgnore.
  1225. // but if it's selected, we check it.
  1226. if(fSuccess==VARIANT_TRUE)
  1227. {
  1228. hr = m_pRangeSelExpand->inRange(m_pRangeChecking, &fSuccess);
  1229. if(FAILED(hr))
  1230. goto error;
  1231. if(fSuccess != VARIANT_TRUE)
  1232. continue;
  1233. }
  1234. }
  1235. tryWordAgain:
  1236. hr = HrGetText(m_pRangeChecking, &pszText);
  1237. if(FAILED(hr))
  1238. goto error;
  1239. if (hr == HR_S_SPELLBREAK)
  1240. break;
  1241. if (hr == HR_S_SPELLCONTINUE)
  1242. continue;
  1243. hr = HrCheckWord(pszText);
  1244. if(FAILED(hr))
  1245. goto error;
  1246. if(m_wsrb.sstat!=sstatNoErrors) //found an error.
  1247. {
  1248. if(FIgnore(m_pRangeChecking))
  1249. {
  1250. m_wsrb.sstat = sstatNoErrors;
  1251. continue;
  1252. }
  1253. // if it contains a period, remove it for processing
  1254. if (StrChr(pszText, '.'))
  1255. {
  1256. BOOL fResult;
  1257. hr = HrStripTrailingPeriod(m_pRangeChecking, &fResult);
  1258. if (FAILED(hr))
  1259. goto error;
  1260. if (fResult)
  1261. goto tryWordAgain;
  1262. }
  1263. HrProcessSpellErrors();
  1264. if(!m_hwndDlg) //spellchecking dialog is lauched only once.
  1265. {
  1266. nCode = DialogBoxParam(g_hLocRes, MAKEINTRESOURCE(iddSpelling), m_hwndNote,
  1267. (DLGPROC) SpellingDlgProc, (LPARAM)this);
  1268. }
  1269. if(nCode == -1)
  1270. hr = E_FAIL;
  1271. else if(FAILED(m_hr))
  1272. // something was wrong when dialog calls HrFindErrors.
  1273. // it has higher priority than IDCANCEL.
  1274. hr = m_hr;
  1275. else if(nCode == IDCANCEL)
  1276. hr = HR_S_SPELLCANCEL;
  1277. else
  1278. // we quit here to pass control to the dialog procedure which calls HrFindErrors.
  1279. hr = HR_S_ABORT;
  1280. goto error;
  1281. }
  1282. }
  1283. error:
  1284. SafeMemFree(pszText);
  1285. return hr;
  1286. }
  1287. HRESULT CSpell::HrGetText(IMarkupPointer* pRangeStart, IMarkupPointer* pRangeEnd, LPSTR *ppszText)
  1288. {
  1289. HRESULT hr = NOERROR;
  1290. IHTMLTxtRange *pTxtRange = NULL;
  1291. BSTR bstr = NULL;
  1292. if (ppszText == NULL || pRangeStart == NULL || pRangeEnd == NULL)
  1293. return E_INVALIDARG;
  1294. *ppszText = NULL;
  1295. hr = _EnsureInited();
  1296. if (FAILED(hr))
  1297. goto error;
  1298. hr = m_pBodyElem->createTextRange(&pTxtRange);
  1299. if (FAILED(hr))
  1300. goto error;
  1301. hr = m_pMarkup->MoveRangeToPointers(pRangeStart, pRangeEnd, pTxtRange);
  1302. if (FAILED(hr))
  1303. goto error;
  1304. hr = pTxtRange->get_text(&bstr);
  1305. if (FAILED(hr))
  1306. goto error;
  1307. if(bstr==NULL || SysStringLen(bstr)==0)
  1308. {
  1309. hr = HR_S_SPELLBREAK;
  1310. goto error;
  1311. }
  1312. // never spell check Japanese.
  1313. if(((WORD)*bstr > (WORD)0x3000) && //DBCS
  1314. (GetACP() == 932 || FIgnoreDBCS()))
  1315. {
  1316. hr = HR_S_SPELLCONTINUE;
  1317. goto error;
  1318. }
  1319. hr = HrBSTRToLPSZ(CP_ACP, bstr, ppszText);
  1320. if (FAILED(hr))
  1321. goto error;
  1322. if (*ppszText == NULL)
  1323. {
  1324. hr = E_FAIL;
  1325. goto error;
  1326. }
  1327. error:
  1328. SafeRelease(pTxtRange);
  1329. SafeSysFreeString(bstr);
  1330. return hr;
  1331. }
  1332. HRESULT CSpell::HrGetText(IHTMLTxtRange* pRange, LPSTR *ppszText)
  1333. {
  1334. BSTR bstr=0;
  1335. HRESULT hr = NOERROR;
  1336. UINT uCodePage;
  1337. if (ppszText==NULL || pRange==NULL)
  1338. return E_INVALIDARG;
  1339. *ppszText = 0;
  1340. hr = pRange->get_text(&bstr);
  1341. if(FAILED(hr))
  1342. goto error;
  1343. if(bstr==NULL || SysStringLen(bstr)==0)
  1344. {
  1345. hr = HR_S_SPELLBREAK;
  1346. goto error;
  1347. }
  1348. // never spell check Japanese.
  1349. if(((WORD)*bstr > (WORD)0x3000) && //DBCS
  1350. (GetACP() == 932 || FIgnoreDBCS()))
  1351. {
  1352. hr = HR_S_SPELLCONTINUE;
  1353. goto error;
  1354. }
  1355. uCodePage = GetCodePage();
  1356. *ppszText = PszToANSI(uCodePage, bstr);
  1357. if (*ppszText == NULL)
  1358. {
  1359. hr = E_FAIL;
  1360. goto error;
  1361. }
  1362. error:
  1363. SafeSysFreeString(bstr);
  1364. return hr;
  1365. }
  1366. HRESULT CSpell::HrStripTrailingPeriod(IHTMLTxtRange* pRange, BOOL* pfResult)
  1367. {
  1368. HRESULT hr = NOERROR;
  1369. IMarkupPointer *pRangeStart = NULL;
  1370. IMarkupPointer *pRangeEnd = NULL;
  1371. IMarkupPointer *pRangeTemp = NULL;
  1372. MARKUP_CONTEXT_TYPE markupContext;
  1373. long cch;
  1374. OLECHAR chText[64];
  1375. BOOL fResult;
  1376. if (pRange==NULL || pfResult == NULL)
  1377. return E_INVALIDARG;
  1378. *pfResult = FALSE;
  1379. hr = _EnsureInited();
  1380. if (FAILED(hr))
  1381. goto error;
  1382. hr = m_pMarkup->CreateMarkupPointer(&pRangeStart);
  1383. if (FAILED(hr))
  1384. goto error;
  1385. hr = m_pMarkup->CreateMarkupPointer(&pRangeEnd);
  1386. if (FAILED(hr))
  1387. goto error;
  1388. hr = m_pMarkup->CreateMarkupPointer(&pRangeTemp);
  1389. if (FAILED(hr))
  1390. goto error;
  1391. hr = m_pMarkup->MovePointersToRange(pRange, pRangeStart, pRangeEnd);
  1392. if (FAILED(hr))
  1393. goto error;
  1394. hr = pRangeStart->IsEqualTo(pRangeEnd, &fResult);
  1395. if (FAILED(hr))
  1396. goto error;
  1397. if (fResult)
  1398. {
  1399. hr = HR_S_SPELLBREAK;
  1400. goto error;
  1401. }
  1402. // check for '.' and remove
  1403. {
  1404. hr = pRangeTemp->MoveToPointer(pRangeEnd);
  1405. if (FAILED(hr))
  1406. goto error;
  1407. while(TRUE)
  1408. {
  1409. cch = 1;
  1410. hr = pRangeTemp->Left(FALSE, &markupContext, NULL, &cch, chText);
  1411. if (FAILED(hr))
  1412. goto error;
  1413. if (markupContext == CONTEXT_TYPE_None)
  1414. goto finished;
  1415. hr = pRangeTemp->IsRightOf(pRangeStart, &fResult);
  1416. if (FAILED(hr))
  1417. goto error;
  1418. if (!fResult)
  1419. {
  1420. hr = HR_S_SPELLBREAK;
  1421. goto error;
  1422. }
  1423. if (markupContext == CONTEXT_TYPE_Text && chText[0] != L'.')
  1424. goto finished;
  1425. cch = 1;
  1426. hr = pRangeTemp->Left(TRUE, NULL, NULL, &cch, NULL);
  1427. if (FAILED(hr))
  1428. goto error;
  1429. if (markupContext == CONTEXT_TYPE_Text)
  1430. {
  1431. hr = pRangeEnd->MoveToPointer(pRangeTemp);
  1432. if (FAILED(hr))
  1433. goto error;
  1434. *pfResult = TRUE;
  1435. }
  1436. }
  1437. }
  1438. finished:
  1439. hr = m_pMarkup->MoveRangeToPointers(pRangeStart, pRangeEnd, pRange);
  1440. if (FAILED(hr))
  1441. goto error;
  1442. error:
  1443. SafeRelease(pRangeStart);
  1444. SafeRelease(pRangeEnd);
  1445. SafeRelease(pRangeTemp);
  1446. return hr;
  1447. }
  1448. HRESULT CSpell::HrHasWhitespace(IMarkupPointer* pRangeStart, IMarkupPointer* pRangeEnd, BOOL *pfResult)
  1449. {
  1450. HRESULT hr = NOERROR;
  1451. LPSTR pszText = NULL;
  1452. LPSTR psz;
  1453. if (pRangeStart == NULL || pRangeEnd == NULL || pfResult == NULL)
  1454. return E_INVALIDARG;
  1455. *pfResult = FALSE;
  1456. hr = HrGetText(pRangeStart, pRangeEnd, &pszText);
  1457. if (FAILED(hr) || HR_S_SPELLCONTINUE == hr || HR_S_SPELLBREAK == hr)
  1458. goto error;
  1459. Assert(NULL != pszText);
  1460. for(psz = pszText; *psz; psz = CharNext(psz))
  1461. {
  1462. if (IsSpace(psz))
  1463. {
  1464. *pfResult = TRUE;
  1465. break;
  1466. }
  1467. }
  1468. error:
  1469. SafeMemFree(pszText);
  1470. return hr;
  1471. }
  1472. HRESULT CSpell::HrGetNextWordRange(IHTMLTxtRange* pRange)
  1473. {
  1474. HRESULT hr = NOERROR;
  1475. IMarkupPointer *pRangeStart = NULL;
  1476. IMarkupPointer *pRangeEnd = NULL;
  1477. IMarkupPointer *pRangeTemp = NULL;
  1478. MARKUP_CONTEXT_TYPE markupContext;
  1479. long cch;
  1480. OLECHAR chText[64];
  1481. BOOL fResult;
  1482. BOOL fFoundWhite;
  1483. if (pRange==NULL)
  1484. return E_INVALIDARG;
  1485. hr = _EnsureInited();
  1486. if (FAILED(hr))
  1487. goto error;
  1488. hr = m_pMarkup->CreateMarkupPointer(&pRangeStart);
  1489. if (FAILED(hr))
  1490. goto error;
  1491. hr = m_pMarkup->CreateMarkupPointer(&pRangeEnd);
  1492. if (FAILED(hr))
  1493. goto error;
  1494. hr = m_pMarkup->CreateMarkupPointer(&pRangeTemp);
  1495. if (FAILED(hr))
  1496. goto error;
  1497. hr = m_pMarkup->MovePointersToRange(pRange, pRangeStart, pRangeEnd);
  1498. if (FAILED(hr))
  1499. goto error;
  1500. hr = pRangeStart->IsEqualTo(pRangeEnd, &fResult);
  1501. if (FAILED(hr))
  1502. goto error;
  1503. if (!fResult)
  1504. {
  1505. do
  1506. {
  1507. hr = pRangeStart->MoveUnit(MOVEUNIT_NEXTWORDBEGIN);
  1508. if (FAILED(hr))
  1509. goto error;
  1510. // make sure beyond the old end
  1511. hr = pRangeStart->IsLeftOf(pRangeEnd, &fResult);
  1512. if (FAILED(hr))
  1513. goto error;
  1514. } while(fResult);
  1515. hr = pRangeEnd->MoveToPointer(pRangeStart);
  1516. if (FAILED(hr))
  1517. goto error;
  1518. }
  1519. hr = pRangeEnd->MoveUnit(MOVEUNIT_NEXTWORDEND);
  1520. if (FAILED(hr))
  1521. goto error;
  1522. processNextWord:
  1523. // check to see if we have anything
  1524. hr = pRangeEnd->IsRightOf(pRangeStart, &fResult);
  1525. if (FAILED(hr))
  1526. goto error;
  1527. // if the end is not to the right of the start then we do not have a word
  1528. if (!fResult)
  1529. {
  1530. hr = HR_S_SPELLBREAK;
  1531. goto error;
  1532. }
  1533. // strip preceding puncts or white space
  1534. // words can also be created with just puncts and whitespace
  1535. {
  1536. while(TRUE)
  1537. {
  1538. cch = 1;
  1539. hr = pRangeStart->Right(FALSE, &markupContext, NULL, &cch, chText);
  1540. if (FAILED(hr))
  1541. goto error;
  1542. if (markupContext == CONTEXT_TYPE_None)
  1543. goto finished;
  1544. hr = pRangeStart->IsLeftOf(pRangeEnd, &fResult);
  1545. if (FAILED(hr))
  1546. goto error;
  1547. if (!fResult)
  1548. {
  1549. hr = pRangeEnd->MoveUnit(MOVEUNIT_NEXTWORDEND);
  1550. if (FAILED(hr))
  1551. goto error;
  1552. continue;
  1553. }
  1554. if (markupContext == CONTEXT_TYPE_Text && 0 == ((C1_SPACE | C1_PUNCT) & GetWCharType(chText[0])))
  1555. break;
  1556. cch = 1;
  1557. hr = pRangeStart->Right(TRUE, NULL, NULL, &cch, NULL);
  1558. if (FAILED(hr))
  1559. goto error;
  1560. }
  1561. }
  1562. // check for white space and remove
  1563. {
  1564. fFoundWhite = FALSE;
  1565. hr = pRangeTemp->MoveToPointer(pRangeEnd);
  1566. if (FAILED(hr))
  1567. goto error;
  1568. while(TRUE)
  1569. {
  1570. cch = 1;
  1571. hr = pRangeTemp->Left(FALSE, &markupContext, NULL, &cch, chText);
  1572. if (FAILED(hr))
  1573. goto error;
  1574. if (markupContext == CONTEXT_TYPE_None)
  1575. goto finished;
  1576. hr = pRangeTemp->IsRightOf(pRangeStart, &fResult);
  1577. if (FAILED(hr))
  1578. goto error;
  1579. if (!fResult)
  1580. {
  1581. hr = HR_S_SPELLBREAK;
  1582. goto error;
  1583. }
  1584. if (markupContext == CONTEXT_TYPE_Text)
  1585. {
  1586. if (0 == (C1_SPACE & GetWCharType(chText[0])))
  1587. {
  1588. if (!fFoundWhite)
  1589. break;
  1590. goto finished;
  1591. }
  1592. fFoundWhite = TRUE;
  1593. }
  1594. cch = 1;
  1595. hr = pRangeTemp->Left(TRUE, NULL, NULL, &cch, NULL);
  1596. if (FAILED(hr))
  1597. goto error;
  1598. if (markupContext == CONTEXT_TYPE_Text)
  1599. {
  1600. hr = pRangeEnd->MoveToPointer(pRangeTemp);
  1601. if (FAILED(hr))
  1602. goto error;
  1603. }
  1604. }
  1605. }
  1606. // now look for a period
  1607. {
  1608. hr = pRangeTemp->MoveToPointer(pRangeEnd);
  1609. if (FAILED(hr))
  1610. goto error;
  1611. while(TRUE)
  1612. {
  1613. cch = 1;
  1614. hr = pRangeTemp->Right(FALSE, &markupContext, NULL, &cch, chText);
  1615. if (FAILED(hr))
  1616. goto error;
  1617. if (markupContext == CONTEXT_TYPE_None)
  1618. goto finished;
  1619. if (markupContext == CONTEXT_TYPE_Text && chText[0] != L'.')
  1620. goto finished;
  1621. cch = 1;
  1622. hr = pRangeTemp->Right(TRUE, NULL, NULL, &cch, NULL);
  1623. if (FAILED(hr))
  1624. goto error;
  1625. if (markupContext == CONTEXT_TYPE_Text && chText[0] == L'.')
  1626. {
  1627. hr = HrHasWhitespace(pRangeStart, pRangeTemp, &fResult);
  1628. if (FAILED(hr))
  1629. goto error;
  1630. if (fResult)
  1631. goto finished;
  1632. hr = pRangeEnd->MoveToPointer(pRangeTemp);
  1633. if (FAILED(hr))
  1634. goto error;
  1635. // scan ahead for characters
  1636. // need to check for i.e. -- abbreviations
  1637. // it sure would be nice if Trident could do this!!
  1638. {
  1639. while(TRUE)
  1640. {
  1641. cch = 1;
  1642. hr = pRangeTemp->Right(FALSE, &markupContext, NULL, &cch, chText);
  1643. if (FAILED(hr))
  1644. goto error;
  1645. if (markupContext == CONTEXT_TYPE_None)
  1646. goto finished;
  1647. if (markupContext == CONTEXT_TYPE_Text && 0 == (C1_SPACE & GetWCharType(chText[0])))
  1648. goto finished;
  1649. cch = 1;
  1650. hr = pRangeTemp->Right(TRUE, NULL, NULL, &cch, NULL);
  1651. if (FAILED(hr))
  1652. goto error;
  1653. // we found more text
  1654. // need to check to see if we crossed white space to get here
  1655. if (markupContext == CONTEXT_TYPE_Text)
  1656. {
  1657. hr = pRangeTemp->MoveToPointer(pRangeEnd);
  1658. if (FAILED(hr))
  1659. goto error;
  1660. hr = pRangeTemp->MoveUnit(MOVEUNIT_NEXTWORDEND);
  1661. if (FAILED(hr))
  1662. goto finished;
  1663. hr = HrHasWhitespace(pRangeStart, pRangeTemp, &fResult);
  1664. if (FAILED(hr))
  1665. goto error;
  1666. if (fResult)
  1667. goto finished;
  1668. pRangeEnd->MoveToPointer(pRangeTemp);
  1669. if (FAILED(hr))
  1670. goto error;
  1671. goto processNextWord;
  1672. }
  1673. }
  1674. }
  1675. goto finished;
  1676. }
  1677. }
  1678. }
  1679. finished:
  1680. hr = m_pMarkup->MoveRangeToPointers(pRangeStart, pRangeEnd, pRange);
  1681. if (FAILED(hr))
  1682. goto error;
  1683. error:
  1684. SafeRelease(pRangeStart);
  1685. SafeRelease(pRangeEnd);
  1686. SafeRelease(pRangeTemp);
  1687. return hr;
  1688. }
  1689. BOOL CSpell::FIgnore(IHTMLTxtRange *pRangeChecking)
  1690. {
  1691. HRESULT hr;
  1692. IHTMLAnchorElement *pAE=0;
  1693. IHTMLElement *pElemParent=0;
  1694. BOOL fRet = FALSE;
  1695. BSTR bstr=0;
  1696. IHTMLTxtRange *pRange=0;
  1697. VARIANT_BOOL fSuccess;
  1698. if(pRangeChecking == NULL)
  1699. return fRet;
  1700. if(FIgnoreURL())
  1701. {
  1702. hr = pRangeChecking->duplicate(&pRange);
  1703. if(FAILED(hr))
  1704. goto Cleanup;
  1705. hr = pRange->collapse(VARIANT_TRUE);
  1706. if(FAILED(hr))
  1707. goto Cleanup;
  1708. hr = pRange->expand((BSTR)c_bstr_Character, &fSuccess);
  1709. if(FAILED(hr))
  1710. goto Cleanup;
  1711. // check pRangeChecking if we are on URL
  1712. hr = pRange->parentElement(&pElemParent);
  1713. if(FAILED(hr))
  1714. goto Cleanup;
  1715. hr = pElemParent->QueryInterface(IID_IHTMLAnchorElement, (LPVOID *)&pAE);
  1716. if(FAILED(hr))
  1717. goto Cleanup;
  1718. hr = pAE->get_href(&bstr);
  1719. if(FAILED(hr))
  1720. goto Cleanup;
  1721. if(bstr != NULL)
  1722. {
  1723. fRet = TRUE;
  1724. goto Cleanup;
  1725. }
  1726. }
  1727. Cleanup:
  1728. ReleaseObj(pElemParent);
  1729. ReleaseObj(pAE);
  1730. ReleaseObj(pRange);
  1731. SafeSysFreeString(bstr);
  1732. return fRet;
  1733. }
  1734. // [email protected] - can now specify dict index - 53193
  1735. HRESULT CSpell::AddToUdrW(WCHAR* pwsz, PROOFLEX lex)
  1736. {
  1737. m_pfnSpellerAddUdr(m_pid, lex, pwsz);
  1738. return NOERROR;
  1739. }
  1740. // [email protected] - can now specify dict index - 53193
  1741. HRESULT CSpell::AddToUdrA(CHAR* psz, PROOFLEX lex)
  1742. {
  1743. WCHAR wszBuf[cchEditBufferMax]={0};
  1744. MultiByteToWideChar(CP_ACP, 0, psz, -1, wszBuf, ARRAYSIZE(wszBuf)-1);
  1745. return AddToUdrW(wszBuf, lex);
  1746. }
  1747. HRESULT CSpell::HrProcessSpellErrors()
  1748. {
  1749. int idSpellErrorString;
  1750. HRESULT hr = S_OK;
  1751. WideCharToMultiByte(GetCodePage(), 0, m_wsib.pwsz, -1, m_szWrongWord, sizeof(m_szWrongWord)-1, NULL, NULL);
  1752. // Select error word in edit control except for Abbreviation warnings
  1753. if (m_wsrb.sstat != sstatWordConsideredAbbreviation && m_pRangeChecking)
  1754. {
  1755. hr = m_pRangeChecking->select();
  1756. if(FAILED(hr))
  1757. goto End;
  1758. }
  1759. // Process spelling error
  1760. if (m_wsrb.sstat == sstatReturningChangeAlways ||
  1761. m_wsrb.sstat == sstatReturningChangeOnce)
  1762. {
  1763. WideCharToMultiByte(GetCodePage(), 0, m_wsrb.pwsz, -1, m_szEdited, sizeof(m_szEdited)-1, NULL, NULL);
  1764. // "Change always" was returned. This means we have to do the replacement
  1765. // automatically and then find the next spelling error.
  1766. if (m_wsrb.sstat==sstatReturningChangeAlways)
  1767. {
  1768. FVerifyThisText(m_szEdited, TRUE);
  1769. m_fCanUndo = FALSE; // can't undo automatic replacements
  1770. hr = HrReplaceErrorText(TRUE, FALSE);
  1771. if (FAILED(hr))
  1772. goto End;
  1773. m_wsrb.sstat = sstatNoErrors;
  1774. HrFindErrors();
  1775. }
  1776. }
  1777. else if (m_wsrb.sstat == sstatWordConsideredAbbreviation)
  1778. {
  1779. // An abbreviation was returned. We need to add it to the IgnoreAlways cache and
  1780. // find the next spelling error.
  1781. AddToUdrW((WCHAR*)m_wsib.pwsz, m_rgprflex[1]);
  1782. m_wsrb.sstat = sstatNoErrors;
  1783. HrFindErrors();
  1784. }
  1785. else
  1786. lstrcpy(m_szEdited, m_szWrongWord);
  1787. // Load the right error description string
  1788. switch (m_wsrb.sstat)
  1789. {
  1790. case sstatUnknownInputWord:
  1791. case sstatReturningChangeOnce:
  1792. case sstatInitialNumeral:
  1793. idSpellErrorString = idsSpellWordNotFound;
  1794. break;
  1795. case sstatRepeatWord:
  1796. idSpellErrorString = idsSpellRepeatWord;
  1797. break;
  1798. case sstatErrorCapitalization:
  1799. idSpellErrorString = idsSpellWordNeedsCap;
  1800. break;
  1801. }
  1802. LoadString(g_hLocRes, idSpellErrorString, m_szErrType,
  1803. sizeof(m_szErrType)/sizeof(TCHAR));
  1804. // Handle suggestions
  1805. m_fSuggestions = FALSE;
  1806. #ifdef __WBK__NEVER__
  1807. if (m_wsrb.sstat == sstatReturningChangeOnce)
  1808. {
  1809. // Automatic suggestion of one word
  1810. m_fSuggestions = TRUE;
  1811. m_fNoneSuggested = FALSE;
  1812. }
  1813. else
  1814. #endif // __WBK__NEVER__
  1815. {
  1816. // Enumerate suggestion list if requested
  1817. if (m_fAlwaysSuggest)
  1818. hr = HrSpellSuggest();
  1819. }
  1820. End:
  1821. return hr;
  1822. }
  1823. HRESULT CSpell::HrReplaceErrorText(BOOL fChangeAll, BOOL fAddToUdr)
  1824. {
  1825. HRESULT hr=NOERROR;
  1826. WCHAR wszWrong[cchEditBufferMax]={0};
  1827. WCHAR wszEdited[cchEditBufferMax]={0};
  1828. int cwch;
  1829. if (fAddToUdr)
  1830. {
  1831. RemoveTrailingSpace(m_szWrongWord);
  1832. cwch = MultiByteToWideChar(GetCodePage(), 0, m_szWrongWord, -1, wszWrong, ARRAYSIZE(wszWrong)-1);
  1833. Assert(cwch);
  1834. cwch = MultiByteToWideChar(GetCodePage(), 0, m_szEdited, -1, wszEdited, ARRAYSIZE(wszEdited)-1);
  1835. Assert(cwch);
  1836. hr = m_pfnSpellerAddChangeUdr(m_pid, fChangeAll ? lxtChangeAlways : lxtChangeOnce, wszWrong, wszEdited);
  1837. if (FAILED(hr))
  1838. goto error;
  1839. }
  1840. hr = HrReplaceSel(m_szEdited);
  1841. if (FAILED(hr))
  1842. goto error;
  1843. error:
  1844. return hr;
  1845. }
  1846. HRESULT CSpell::HrCheckWord(LPCSTR pszWord)
  1847. {
  1848. DWORD cwchWord;
  1849. PTEC ptec;
  1850. SPELLERSUGGESTION sugg;
  1851. cwchWord = MultiByteToWideChar(GetCodePage(), 0, pszWord, -1, m_wszIn, ARRAYSIZE(m_wszIn)-1);
  1852. ZeroMemory(&m_wsrb, sizeof(m_wsrb));
  1853. ZeroMemory(&m_wsib, sizeof(m_wsib));
  1854. m_wsib.pwsz = m_wszIn;
  1855. m_wsib.cch = cwchWord;
  1856. m_wsib.clex = m_clex;
  1857. m_wsib.prglex = m_rgprflex;
  1858. m_wsib.ichStart = 0;
  1859. m_wsib.cchUse = cwchWord;
  1860. m_wsrb.pwsz = m_wszRet;
  1861. m_wsrb.cchAlloc = ARRAYSIZE(m_wszRet);
  1862. m_wsrb.cszAlloc = 1; // we've got space for 1 speller suggestion
  1863. m_wsrb.prgsugg = &sugg;
  1864. // [email protected] - "repeat word" bug fix - 2757, 13573, 56057
  1865. // m_wsib.sstate should only be sstateIsContinued after the first call to this function
  1866. // (e.g., when a new speller session is invoked using F7 or the menu item).
  1867. // This allows the spell code to accurately track "repeat" words.
  1868. if (m_fSpellContinue)
  1869. m_wsib.sstate = sstateIsContinued;
  1870. else
  1871. m_fSpellContinue = TRUE;
  1872. ptec = m_pfnSpellerCheck(m_pid, scmdVerifyBuffer, &m_wsib, &m_wsrb);
  1873. // do we haveinvalid characters, if so return NOERR
  1874. if (ProofMajorErr(ptec) != ptecNoErrors && ProofMinorErr(ptec) == ptecInvalidEntry)
  1875. {
  1876. // force it to be correct
  1877. m_wsrb.sstat = sstatNoErrors;
  1878. return NOERROR;
  1879. }
  1880. if (ptec != ptecNoErrors)
  1881. return E_FAIL;
  1882. return NOERROR;
  1883. }
  1884. HRESULT CSpell::HrSpellSuggest()
  1885. {
  1886. int cchWord;
  1887. WCHAR wszBuff[cchMaxSuggestBuff]={0};
  1888. WCHAR wszWord[cchEditBufferMax]={0};
  1889. SPELLERSUGGESTION rgsugg[20];
  1890. TCHAR *pchNextSlot=0;
  1891. ULONG iszSuggestion;
  1892. int cchSuggestion;
  1893. SPELLERSUGGESTION *pSuggestion;
  1894. TCHAR *pchLim=0;
  1895. PTEC ptec;
  1896. SPELLERSTATUS sstat;
  1897. sstat = m_wsrb.sstat;
  1898. cchWord = MultiByteToWideChar(GetCodePage(), 0, m_szEdited, -1, wszWord, ARRAYSIZE(wszWord)-1);
  1899. m_wsib.cch = cchWord;
  1900. m_wsib.clex = m_clex;
  1901. m_wsib.prglex = m_rgprflex;
  1902. m_wsib.ichStart = 0;
  1903. m_wsib.cchUse = cchWord;
  1904. m_wsib.pwsz = wszWord;
  1905. m_wsrb.prgsugg = rgsugg;
  1906. m_wsrb.cszAlloc = ARRAYSIZE(rgsugg);
  1907. m_wsrb.pwsz = wszBuff;
  1908. m_wsrb.cchAlloc = ARRAYSIZE(wszBuff);
  1909. ptec = m_pfnSpellerCheck(m_pid, scmdSuggest, &m_wsib, &m_wsrb);
  1910. m_fNoneSuggested = (m_wsrb.csz == 0);
  1911. pchLim = &m_szSuggest[ARRAYSIZE(m_szSuggest)-1];
  1912. pchNextSlot = m_szSuggest;;
  1913. do
  1914. {
  1915. pSuggestion = m_wsrb.prgsugg;
  1916. if (sstatMoreInfoThanBufferCouldHold == m_wsrb.sstat)
  1917. {
  1918. m_wsrb.csz = m_wsrb.cszAlloc;
  1919. }
  1920. for (iszSuggestion = 0; iszSuggestion < m_wsrb.csz; iszSuggestion++)
  1921. {
  1922. cchSuggestion = WideCharToMultiByte(GetCodePage(), 0, pSuggestion->pwsz, -1,
  1923. pchNextSlot, (int) (pchLim-pchNextSlot), NULL, NULL);
  1924. // [email protected] - raid 29322
  1925. // make sure words do not have trailing spaces
  1926. // only the French speller returns words with trailing spaces
  1927. RemoveTrailingSpace(pchNextSlot);
  1928. cchSuggestion = lstrlen(pchNextSlot)+1;
  1929. pSuggestion++;
  1930. if (cchSuggestion > 0)
  1931. pchNextSlot += cchSuggestion;
  1932. Assert(pchNextSlot <= pchLim);
  1933. }
  1934. ptec = m_pfnSpellerCheck(m_pid, scmdSuggestMore, &m_wsib, &m_wsrb);
  1935. } while (ptec == ptecNoErrors && m_wsrb.sstat!=sstatNoMoreSuggestions);
  1936. *pchNextSlot = '\0';
  1937. m_wsrb.sstat = sstat;
  1938. m_fSuggestions = TRUE;
  1939. return NOERROR;
  1940. }
  1941. VOID CSpell::FillSuggestLbx()
  1942. {
  1943. HWND hwndLbx;
  1944. INT isz;
  1945. LPTSTR sz;
  1946. LPTSTR pch;
  1947. // Empty contents regardless
  1948. hwndLbx = GetDlgItem(m_hwndDlg, LBX_Spell_Suggest);
  1949. ListBox_ResetContent(hwndLbx);
  1950. // We didn't even try to get any suggestions
  1951. if (!m_fSuggestions)
  1952. return;
  1953. // We tried to get suggestions
  1954. pch = m_szSuggest;
  1955. if (*pch == '\0')
  1956. {
  1957. LoadString(g_hLocRes, idsSpellNoSuggestions, m_szTempBuffer,
  1958. sizeof(m_szTempBuffer)/sizeof(TCHAR));
  1959. ListBox_AddString(hwndLbx, m_szTempBuffer);
  1960. }
  1961. else
  1962. {
  1963. while(*pch != '\0')
  1964. {
  1965. ListBox_AddString(hwndLbx, pch);
  1966. while(*pch != '\0')
  1967. pch++;
  1968. pch++;
  1969. }
  1970. }
  1971. }
  1972. VOID UpdateEditedFromSuggest(HWND hwndDlg, HWND hwndEdited, HWND hwndSuggest)
  1973. {
  1974. INT nSel;
  1975. INT cch;
  1976. LPSTR szTemp;
  1977. nSel = ListBox_GetCurSel(hwndSuggest);
  1978. cch = ListBox_GetTextLen(hwndSuggest, nSel) + 1;
  1979. if (MemAlloc((LPVOID *) &szTemp, cch))
  1980. {
  1981. ListBox_GetText(hwndSuggest, nSel, szTemp);
  1982. SetWindowText(hwndEdited, szTemp);
  1983. // Clear default button style from "Ignore" button and set default to "Change"
  1984. Button_SetStyle(GetDlgItem(hwndDlg, PSB_Spell_Ignore), BS_PUSHBUTTON, TRUE);
  1985. SendMessage(hwndDlg, DM_SETDEFID, PSB_Spell_Change, 0L);
  1986. Button_SetStyle(GetDlgItem(hwndDlg, PSB_Spell_Change), BS_DEFPUSHBUTTON, TRUE);
  1987. Edit_SetSel(hwndEdited, 0, 32767); // select the whole thing
  1988. Edit_SetModify(hwndEdited, TRUE);
  1989. MemFree(szTemp);
  1990. }
  1991. }
  1992. BOOL CSpell::FVerifyThisText(LPSTR szThisText, BOOL /*fProcessOnly*/)
  1993. {
  1994. BOOL fReturn=FALSE;
  1995. HRESULT hr;
  1996. Assert(szThisText);
  1997. hr = HrCheckWord(szThisText);
  1998. if (FAILED(hr))
  1999. goto error;
  2000. switch (m_wsrb.sstat)
  2001. {
  2002. case sstatUnknownInputWord:
  2003. case sstatInitialNumeral:
  2004. case sstatErrorCapitalization:
  2005. if (AthMessageBoxW(m_hwndDlg, MAKEINTRESOURCEW(idsSpellCaption), MAKEINTRESOURCEW(idsSpellMsgConfirm), NULL, MB_YESNO | MB_ICONEXCLAMATION ) == IDYES)
  2006. fReturn = TRUE;
  2007. else
  2008. fReturn = FALSE;
  2009. break;
  2010. default:
  2011. fReturn = TRUE;
  2012. break;
  2013. }
  2014. error:
  2015. return fReturn;
  2016. }
  2017. VOID CSpell::SpellSaveUndo(INT idButton)
  2018. {
  2019. HRESULT hr = NOERROR;
  2020. if(!m_pRangeChecking)
  2021. return;
  2022. SafeRelease(m_pRangeUndoSave);
  2023. m_pRangeChecking->duplicate(&m_pRangeUndoSave);
  2024. if(!m_pRangeUndoSave)
  2025. goto error;
  2026. m_fCanUndo = TRUE;
  2027. error:
  2028. return;
  2029. }
  2030. VOID CSpell::SpellDoUndo()
  2031. {
  2032. HRESULT hr = NOERROR;
  2033. IOleCommandTarget* pCmdTarget = NULL;
  2034. CHARRANGE chrg = {0};
  2035. LONG lMin = 0;
  2036. m_fCanUndo = FALSE;
  2037. if(!m_pRangeUndoSave)
  2038. goto Cleanup;
  2039. SafeRelease(m_pRangeChecking);
  2040. m_pRangeUndoSave->duplicate(&m_pRangeChecking);
  2041. if(!m_pRangeChecking)
  2042. goto Cleanup;
  2043. hr = m_pRangeChecking->collapse(VARIANT_TRUE);
  2044. if(FAILED(hr))
  2045. goto Cleanup;
  2046. if (m_fUndoChange)
  2047. {
  2048. m_fUndoChange = FALSE;
  2049. hr = m_pDoc->QueryInterface(IID_IOleCommandTarget, (LPVOID *)&pCmdTarget);
  2050. if(FAILED(hr))
  2051. goto Cleanup;
  2052. hr = pCmdTarget->Exec(&CMDSETID_Forms3,
  2053. IDM_UNDO,
  2054. MSOCMDEXECOPT_DONTPROMPTUSER,
  2055. NULL, NULL);
  2056. if(FAILED(hr))
  2057. goto Cleanup;
  2058. }
  2059. Cleanup:
  2060. ReleaseObj(pCmdTarget);
  2061. }
  2062. CSpell::CSpell(IHTMLDocument2* pDoc, IOleCommandTarget* pParentCmdTarget, DWORD dwSpellOpt)
  2063. {
  2064. HRESULT hr;
  2065. Assert(pDoc);
  2066. m_pDoc = pDoc;
  2067. m_pDoc->AddRef();
  2068. Assert(pParentCmdTarget);
  2069. m_pParentCmdTarget = pParentCmdTarget;
  2070. m_pParentCmdTarget->AddRef();
  2071. m_hwndDlg = NULL;
  2072. m_cRef = 1;
  2073. m_fSpellContinue = FALSE;
  2074. m_fCanUndo = FALSE;
  2075. m_fUndoChange = FALSE;
  2076. m_State = SEL;
  2077. m_pRangeDocStartSelStart = NULL;
  2078. m_pRangeSel = NULL;
  2079. m_pRangeSelExpand = NULL;
  2080. m_pRangeSelEndDocEnd = NULL;
  2081. m_pRangeChecking = NULL;
  2082. m_pRangeUndoSave = NULL;
  2083. m_hr = NOERROR;
  2084. m_hinstDll = NULL;
  2085. ZeroMemory(&m_wsib, sizeof(m_wsib));
  2086. ZeroMemory(&m_wsrb, sizeof(m_wsrb));
  2087. ZeroMemory(&m_pid, sizeof(m_pid));
  2088. m_fIgnoreScope = FALSE;
  2089. m_dwCookieNotify = 0;
  2090. m_dwOpt = dwSpellOpt;
  2091. m_langid = lidUnknown;
  2092. m_clex = 0;
  2093. ZeroMemory(&m_rgprflex, sizeof(m_rgprflex));
  2094. m_pMarkup = NULL;
  2095. m_pBodyElem = NULL;
  2096. m_fCSAPI3T1 = FALSE;
  2097. }
  2098. CSpell::~CSpell()
  2099. {
  2100. CloseSpeller();
  2101. SafeRelease(m_pDoc);
  2102. SafeRelease(m_pParentCmdTarget);
  2103. SafeRelease(m_pMarkup);
  2104. SafeRelease(m_pBodyElem);
  2105. }
  2106. ULONG CSpell::AddRef()
  2107. {
  2108. return ++m_cRef;
  2109. }
  2110. ULONG CSpell::Release()
  2111. {
  2112. if (--m_cRef==0)
  2113. {
  2114. delete this;
  2115. return 0;
  2116. }
  2117. return m_cRef;
  2118. }
  2119. HRESULT CSpell::QueryInterface(REFIID riid, LPVOID *lplpObj)
  2120. {
  2121. if(!lplpObj)
  2122. return E_INVALIDARG;
  2123. *lplpObj = NULL; // set to NULL, in case we fail.
  2124. if (IsEqualIID(riid, IID_IUnknown))
  2125. *lplpObj = (LPVOID)this;
  2126. else if (IsEqualIID(riid, IID_IDispatch))
  2127. *lplpObj = (LPVOID)(IDispatch*)this;
  2128. else
  2129. return E_NOINTERFACE;
  2130. AddRef();
  2131. return NOERROR;
  2132. }
  2133. /////////////////////////////////////////////////////////////////////////////
  2134. //
  2135. // IDispatch
  2136. //
  2137. /////////////////////////////////////////////////////////////////////////////
  2138. STDMETHODIMP CSpell::GetIDsOfNames(
  2139. REFIID /*riid*/,
  2140. OLECHAR ** /*rgszNames*/,
  2141. UINT /*cNames*/,
  2142. LCID /*lcid*/,
  2143. DISPID * /*rgDispId*/)
  2144. {
  2145. return E_NOTIMPL;
  2146. }
  2147. STDMETHODIMP CSpell::GetTypeInfo(
  2148. UINT /*iTInfo*/,
  2149. LCID /*lcid*/,
  2150. ITypeInfo **ppTInfo)
  2151. {
  2152. if (ppTInfo)
  2153. *ppTInfo=NULL;
  2154. return E_NOTIMPL;
  2155. }
  2156. STDMETHODIMP CSpell::GetTypeInfoCount(UINT *pctinfo)
  2157. {
  2158. if (pctinfo)
  2159. {
  2160. *pctinfo=0;
  2161. return NOERROR;
  2162. }
  2163. else
  2164. return E_POINTER;
  2165. }
  2166. #ifdef BACKGROUNDSPELL
  2167. STDMETHODIMP CSpell::Invoke(
  2168. DISPID dispIdMember,
  2169. REFIID /*riid*/,
  2170. LCID /*lcid*/,
  2171. WORD wFlags,
  2172. DISPPARAMS FAR* /*pDispParams*/,
  2173. VARIANT * /*pVarResult*/,
  2174. EXCEPINFO * /*pExcepInfo*/,
  2175. UINT * /*puArgErr*/)
  2176. {
  2177. IHTMLWindow2 *pWindow=0;
  2178. IHTMLEventObj *pEvent=0;
  2179. BSTR bstr=0;
  2180. HRESULT hr=E_NOTIMPL;
  2181. LONG lKeyCode=0;
  2182. LONG cb;
  2183. if (dispIdMember == DISPID_HTMLDOCUMENTEVENTS_ONKEYUP &&
  2184. (wFlags & DISPATCH_METHOD))
  2185. {
  2186. // Order of events:
  2187. // document gives us window gives us event object
  2188. // the event object can tell us which button was clicked
  2189. // event gives us source element gives us ID
  2190. // a couple lstrcmps will tell us which one got hit
  2191. if (!m_pDoc)
  2192. return E_UNEXPECTED;
  2193. m_pDoc->get_parentWindow(&pWindow);
  2194. if (pWindow)
  2195. {
  2196. pWindow->get_event(&pEvent);
  2197. if (pEvent)
  2198. {
  2199. pEvent->get_keyCode(&lKeyCode);
  2200. if (lKeyCode == 32 || lKeyCode == 188/*','*/ || lKeyCode == 190/*'.'*/ || lKeyCode == 185/*':'*/ || lKeyCode == 186/*';'*/)
  2201. {
  2202. IHTMLTxtRange *pTxtRange=0;
  2203. VARIANT_BOOL fSuccess;
  2204. GetSelection(&pTxtRange);
  2205. if (pTxtRange)
  2206. {
  2207. pTxtRange->move((BSTR)c_bstr_Character, -2, &cb);
  2208. pTxtRange->expand((BSTR)c_bstr_Word, &fSuccess);
  2209. //DumpRange(pRangeDup);
  2210. //pTxtRange->setEndPoint((BSTR)c_bstr_StartToStart, pRangeDup);
  2211. //DumpRange(pTxtRange);
  2212. //pRangeDup->Release();
  2213. m_Stack.push(pTxtRange);
  2214. pTxtRange->Release();
  2215. }
  2216. }
  2217. else if (lKeyCode == 8 /*backspace*/|| lKeyCode == 46/*del*/)
  2218. {
  2219. IHTMLTxtRange *pTxtRange=0;
  2220. VARIANT_BOOL fSuccess;
  2221. LONG cb;
  2222. GetSelection(&pTxtRange);
  2223. if (pTxtRange)
  2224. {
  2225. pTxtRange->expand((BSTR)c_bstr_Word, &fSuccess);
  2226. if (HrHasSquiggle(pTxtRange)==S_OK)
  2227. {
  2228. //DumpRange(pTxtRange);
  2229. m_Stack.push(pTxtRange);
  2230. }
  2231. pTxtRange->Release();
  2232. }
  2233. }
  2234. pEvent->Release();
  2235. }
  2236. pWindow->Release();
  2237. }
  2238. }
  2239. else if (dispIdMember == DISPID_HTMLDOCUMENTEVENTS_ONKEYPRESS && (wFlags & DISPATCH_METHOD))
  2240. {
  2241. if (!m_pDoc)
  2242. return E_UNEXPECTED;
  2243. m_pDoc->get_parentWindow(&pWindow);
  2244. if (pWindow)
  2245. {
  2246. pWindow->get_event(&pEvent);
  2247. if (pEvent)
  2248. {
  2249. pEvent->get_keyCode(&lKeyCode);
  2250. if (lKeyCode == 18)// CTRL+R
  2251. {
  2252. IHTMLTxtRange* pRangeDoc = NULL;
  2253. if (m_pBodyElem)
  2254. m_pBodyElem->createTextRange(&pRangeDoc);
  2255. if (pRangeDoc)
  2256. {
  2257. pRangeDoc->move((BSTR)c_bstr_Character, -1, &cb);
  2258. m_Stack.push(pRangeDoc);
  2259. pRangeDoc->Release();
  2260. }
  2261. }
  2262. pEvent->Release();
  2263. }
  2264. pWindow->Release();
  2265. }
  2266. }
  2267. return hr;
  2268. }
  2269. #endif // BACKGROUNDSPELL
  2270. #ifdef BACKGROUNDSPELL
  2271. HRESULT CSpell::HrHasSquiggle(IHTMLTxtRange *pTxtRange)
  2272. {
  2273. BSTR bstr=0;
  2274. HRESULT hr;
  2275. LPWSTR pwszSquiggleStart=0, pwszSquiggleEnd=0, pwszSquiggleAfter=0;
  2276. hr = pTxtRange->get_htmlText(&bstr);
  2277. if(FAILED(hr) || bstr==0 || *bstr==L'\0')
  2278. {
  2279. hr = S_FALSE;
  2280. goto error;
  2281. }
  2282. hr = S_FALSE;
  2283. pwszSquiggleStart = StrStrIW(bstr, L"<SPAN class=badspelling");
  2284. if (pwszSquiggleStart)
  2285. {
  2286. pwszSquiggleEnd = StrStrIW(bstr, L"</SPAN>");
  2287. if (pwszSquiggleEnd)
  2288. {
  2289. pwszSquiggleAfter = pwszSquiggleEnd + 7;
  2290. if (*pwszSquiggleAfter == L' ' || *pwszSquiggleAfter == L'\0' || *pwszSquiggleAfter == L'&')
  2291. hr = S_OK;
  2292. }
  2293. }
  2294. error:
  2295. SafeSysFreeString(bstr);
  2296. return hr;
  2297. }
  2298. #endif // BACKGROUNDSPELL
  2299. BOOL CSpell::OpenSpeller()
  2300. {
  2301. SpellerParams params;
  2302. DWORD dwSel;
  2303. LANGID langid;
  2304. // LoadOldSpeller is called within LoadNewSpeller
  2305. // We should be checking for V1 spellers after failing
  2306. // for the desired V3 speller, then go on to check for
  2307. // default speller and the speller for 1033.
  2308. if (!LoadNewSpeller())
  2309. goto error;
  2310. if (!OpenUserDictionaries())
  2311. goto error;
  2312. dwSel = sobitStdOptions;
  2313. m_fAlwaysSuggest = !!FAlwaysSuggest();
  2314. if (FIgnoreNumber())
  2315. dwSel |= sobitIgnoreMixedDigits;
  2316. else
  2317. dwSel &= ~sobitIgnoreMixedDigits;
  2318. if (FIgnoreUpper())
  2319. dwSel |= sobitIgnoreAllCaps;
  2320. else
  2321. dwSel &= ~sobitIgnoreAllCaps;
  2322. if (m_pfnSpellerSetOptions(m_pid, soselBits, dwSel) != ptecNoErrors)
  2323. goto error;
  2324. return TRUE;
  2325. error:
  2326. CloseSpeller();
  2327. return FALSE;
  2328. }
  2329. BOOL FNewer(WORD *pwVerOld, WORD *pwVerNew)
  2330. {
  2331. BOOL fOK = FALSE;
  2332. Assert(pwVerOld);
  2333. Assert(pwVerNew);
  2334. if (pwVerNew[0] > pwVerOld[0])
  2335. fOK = TRUE;
  2336. else if (pwVerNew[0] == pwVerOld[0])
  2337. {
  2338. if (pwVerNew[1] > pwVerOld[1])
  2339. fOK = TRUE;
  2340. else if (pwVerNew[1] == pwVerOld[1])
  2341. {
  2342. if (pwVerNew[2] > pwVerOld[2])
  2343. fOK = TRUE;
  2344. else if (pwVerNew[2] == pwVerOld[2])
  2345. {
  2346. if (pwVerNew[3] >= pwVerOld[3])
  2347. fOK = TRUE;
  2348. }
  2349. }
  2350. }
  2351. return fOK;
  2352. }
  2353. BOOL GetDllVersion(LPTSTR pszDll, WORD *pwVer)
  2354. {
  2355. Assert(pszDll);
  2356. Assert(pwVer);
  2357. BOOL fOK = FALSE;
  2358. DWORD dwVerInfoSize, dwVerHnd;
  2359. LPSTR pszInfo, pszVersion, pszT;
  2360. LPWORD pwTrans;
  2361. UINT uLen;
  2362. char szGet[MAX_PATH];
  2363. int i;
  2364. ZeroMemory(pwVer, 4 * sizeof(WORD));
  2365. if (dwVerInfoSize = GetFileVersionInfoSize(pszDll, &dwVerHnd))
  2366. {
  2367. if (pszInfo = (LPSTR)GlobalAlloc(GPTR, dwVerInfoSize))
  2368. {
  2369. if (GetFileVersionInfo(pszDll, dwVerHnd, dwVerInfoSize, pszInfo))
  2370. {
  2371. if (VerQueryValue(pszInfo, "\\VarFileInfo\\Translation", (LPVOID*)&pwTrans, &uLen) &&
  2372. uLen >= (2 * sizeof(WORD)))
  2373. {
  2374. // set up buffer for calls to VerQueryValue()
  2375. wsprintf(szGet, "\\StringFileInfo\\%04X%04X\\FileVersion", pwTrans[0], pwTrans[1]);
  2376. if (VerQueryValue(pszInfo, szGet, (LPVOID *)&pszVersion, &uLen) && uLen)
  2377. {
  2378. i = 0;
  2379. while (*pszVersion)
  2380. {
  2381. if ((',' == *pszVersion) || ('.' == *pszVersion))
  2382. i++;
  2383. else
  2384. {
  2385. pwVer[i] *= 10;
  2386. pwVer[i] += (*pszVersion - '0');
  2387. }
  2388. pszVersion++;
  2389. }
  2390. fOK = TRUE;
  2391. }
  2392. }
  2393. }
  2394. GlobalFree((HGLOBAL)pszInfo);
  2395. }
  2396. }
  2397. return fOK;
  2398. }
  2399. HINSTANCE LoadCSAPI3T1()
  2400. {
  2401. static BOOL s_fInit = FALSE;
  2402. HINSTANCE hinstLocal;
  2403. EnterCriticalSection(&g_csCSAPI3T1);
  2404. // Avoid doing this for every note!
  2405. if (!s_fInit)
  2406. {
  2407. typedef enum
  2408. {
  2409. CSAPI_FIRST,
  2410. CSAPI_DARWIN = CSAPI_FIRST,
  2411. CSAPI_COMMON,
  2412. CSAPI_OE,
  2413. CSAPI_MAX,
  2414. } CSAPISRC;
  2415. BOOL fCheck;
  2416. // cb is for BYTE counts, cch for CHARACTER counts
  2417. DWORD cbDllPath;
  2418. DWORD cchDllPath;
  2419. int csapisrc;
  2420. // Info about the dll currently being examined
  2421. TCHAR szDllPath[MAX_PATH];
  2422. WORD wVer[4] = {0};
  2423. // Info about the dll we will ultimately load
  2424. TCHAR szNewestDllPath[MAX_PATH];
  2425. WORD wVerNewest[4] = {0};
  2426. szDllPath[0] = TEXT('\0');
  2427. szNewestDllPath[0] = TEXT('\0');
  2428. // Avoid doing this for every note!
  2429. s_fInit = TRUE;
  2430. for (csapisrc = CSAPI_FIRST; csapisrc < CSAPI_MAX; csapisrc++)
  2431. {
  2432. // Assume we can't find the dll using the current method, so there's no need to look at its version
  2433. fCheck = FALSE;
  2434. switch (csapisrc)
  2435. {
  2436. // see if Darwin knows where it is
  2437. case CSAPI_DARWIN:
  2438. {
  2439. UINT installState;
  2440. cchDllPath = ARRAYSIZE(szDllPath);
  2441. #ifdef DEBUG
  2442. installState = MsiLocateComponent(CSAPI3T1_DEBUG_GUID, szDllPath, &cchDllPath);
  2443. if (installState != INSTALLSTATE_LOCAL)
  2444. {
  2445. cchDllPath = ARRAYSIZE(szDllPath);
  2446. installState = MsiLocateComponent(CSAPI3T1_GUID, szDllPath, &cchDllPath);
  2447. }
  2448. #else // DEBUG
  2449. installState = MsiLocateComponent(CSAPI3T1_GUID, szDllPath, &cchDllPath);
  2450. #endif // DEBUG
  2451. // Only bother looking at the version if dll is installed
  2452. fCheck = (INSTALLSTATE_LOCAL == installState);
  2453. }
  2454. break;
  2455. // Is it in Common Files\Microsoft Shared\Proof?
  2456. case CSAPI_COMMON:
  2457. {
  2458. DWORD dwType;
  2459. HKEY hkey = NULL;
  2460. LPTSTR pszEnd;
  2461. if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegSharedTools, 0, KEY_QUERY_VALUE, &hkey))
  2462. {
  2463. cbDllPath = sizeof(szDllPath);
  2464. if (SHQueryValueEx(hkey, c_szRegSharedToolsPath, 0L, &dwType, szDllPath, &cbDllPath) == ERROR_SUCCESS)
  2465. {
  2466. pszEnd = PathAddBackslash(szDllPath);
  2467. lstrcpy(pszEnd, c_szSpellCSAPI3T1Path);
  2468. fCheck = TRUE;
  2469. }
  2470. RegCloseKey(hkey);
  2471. }
  2472. }
  2473. break;
  2474. // Is it in the OE directory?
  2475. case CSAPI_OE:
  2476. {
  2477. DWORD dwType;
  2478. HKEY hkey = NULL;
  2479. LPTSTR pszEnd;
  2480. if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegFlat, 0, KEY_QUERY_VALUE, &hkey))
  2481. {
  2482. cbDllPath = sizeof(szDllPath);
  2483. if (SHQueryValueEx(hkey, c_szInstallRoot, 0L, &dwType, szDllPath, &cbDllPath) == ERROR_SUCCESS)
  2484. {
  2485. pszEnd = PathAddBackslash(szDllPath);
  2486. lstrcpy(pszEnd, c_szCSAPI3T1);
  2487. fCheck = TRUE;
  2488. }
  2489. RegCloseKey(hkey);
  2490. }
  2491. }
  2492. break;
  2493. default:
  2494. AssertSz(FALSE, "Unhandled case hit while looking for csapi3t1.dll!");
  2495. break;
  2496. }
  2497. // Figure out the version of the dll if needed
  2498. if (fCheck && GetDllVersion(szDllPath, wVer))
  2499. {
  2500. // If it's newer, remember the new version and the file's location
  2501. if (FNewer(wVerNewest, wVer))
  2502. {
  2503. CopyMemory(wVerNewest, wVer, sizeof(wVer));
  2504. lstrcpy(szNewestDllPath, szDllPath);
  2505. }
  2506. }
  2507. }
  2508. // Assuming we found something, try to load it
  2509. if (szNewestDllPath[0])
  2510. g_hinstCSAPI3T1 = LoadLibrary(szNewestDllPath);
  2511. }
  2512. hinstLocal = g_hinstCSAPI3T1;
  2513. LeaveCriticalSection(&g_csCSAPI3T1);
  2514. return hinstLocal;
  2515. }
  2516. BOOL CSpell::LoadOldSpeller()
  2517. {
  2518. TCHAR szLangId[MAX_PATH] = {0};
  2519. TCHAR rgchBufKeyTest[MAX_PATH] = {0};
  2520. TCHAR rgchBuf[MAX_PATH] = {0};
  2521. WCHAR rgchBufW[MAX_PATH] = {0};
  2522. TCHAR rgchLex[MAX_PATH] = {0};
  2523. WCHAR rgchLexW[MAX_PATH] = {0};
  2524. WCHAR rgchUserDictW[MAX_PATH]={0};
  2525. PROOFLEXIN plxin;
  2526. PROOFLEXOUT plxout;
  2527. SpellerParams params;
  2528. LANGID langid;
  2529. m_hinstDll = LoadCSAPI3T1();
  2530. if (!m_hinstDll)
  2531. {
  2532. m_pfnSpellerCloseLex = 0;
  2533. m_pfnSpellerTerminate = 0;
  2534. return FALSE;
  2535. }
  2536. // We are using the global csapi3t1.dll, so don't free it!
  2537. m_fCSAPI3T1 = TRUE;
  2538. GetAddr(m_pfnSpellerSetDllName, PROOFSETDLLNAME,"SpellerSetDllName");
  2539. GetAddr(m_pfnSpellerVersion, PROOFVERSION, "SpellerVersion");
  2540. GetAddr(m_pfnSpellerInit, PROOFINIT, "SpellerInit");
  2541. GetAddr(m_pfnSpellerTerminate, PROOFTERMINATE, "SpellerTerminate");
  2542. GetAddr(m_pfnSpellerSetOptions, PROOFSETOPTIONS,"SpellerSetOptions");
  2543. GetAddr(m_pfnSpellerOpenLex, PROOFOPENLEX, "SpellerOpenLex");
  2544. GetAddr(m_pfnSpellerCloseLex, PROOFCLOSELEX, "SpellerCloseLex");
  2545. GetAddr(m_pfnSpellerCheck, SPELLERCHECK, "SpellerCheck");
  2546. GetAddr(m_pfnSpellerAddUdr, SPELLERADDUDR, "SpellerAddUdr");
  2547. GetAddr(m_pfnSpellerBuiltInUdr, SPELLERBUILTINUDR, "SpellerBuiltinUdr");
  2548. GetAddr(m_pfnSpellerAddChangeUdr, SPELLERADDCHANGEUDR, "SpellerAddChangeUdr");
  2549. langid = WGetLangID(m_pParentCmdTarget);
  2550. wsprintf(szLangId, "%d", langid);
  2551. wsprintf(rgchBufKeyTest, c_szRegSpellKeyDef, szLangId);
  2552. GetSpellingPaths(rgchBufKeyTest, rgchBuf, rgchLex, sizeof(rgchBuf)/sizeof(TCHAR));
  2553. if (!*rgchBuf)
  2554. return FALSE;
  2555. MultiByteToWideChar(GetCodePage(), 0, rgchBuf, -1, rgchBufW, ARRAYSIZE(rgchBufW)-1);
  2556. m_pfnSpellerSetDllName(rgchBufW, GetCodePage());
  2557. params.versionAPI = PROOFTHISAPIVERSION;
  2558. if (m_pfnSpellerInit(&m_pid, &params) != ptecNoErrors)
  2559. return FALSE;
  2560. m_langid = langid;
  2561. // Tell the speller the name of the dictionary. This requires unicode conversion.
  2562. MultiByteToWideChar(CP_ACP, 0, rgchLex, -1, rgchLexW, ARRAYSIZE(rgchLexW)-1);
  2563. // open the main dict
  2564. plxin.pwszLex = rgchLexW;
  2565. plxin.fCreate = FALSE;
  2566. plxin.lxt = lxtMain;
  2567. plxin.lidExpected = langid;
  2568. memset(&plxout, 0, sizeof(plxout));
  2569. if (m_pfnSpellerOpenLex(m_pid, &plxin, &plxout) != ptecNoErrors)
  2570. return FALSE;
  2571. m_rgprflex[0] = plxout.lex;
  2572. m_clex++;
  2573. return TRUE;
  2574. // needed by the GetAddr macro -- bite me!!!!!!
  2575. error:
  2576. return FALSE;
  2577. }
  2578. BOOL CSpell::LoadNewSpeller()
  2579. {
  2580. SpellerParams params;
  2581. LANGID langid;
  2582. TCHAR rgchEngine[MAX_PATH];
  2583. int cchEngine = sizeof(rgchEngine) / sizeof(rgchEngine[0]);
  2584. TCHAR rgchLex[MAX_PATH];
  2585. int cchLex = sizeof(rgchLex) / sizeof(rgchLex[0]);
  2586. langid = WGetLangID(m_pParentCmdTarget);
  2587. if (!GetNewSpellerEngine(langid, rgchEngine, cchEngine, rgchLex, cchLex))
  2588. {
  2589. if (!LoadOldSpeller())
  2590. {
  2591. langid = GetSystemDefaultLangID();
  2592. if (!GetNewSpellerEngine(langid, rgchEngine, cchEngine, rgchLex, cchLex))
  2593. {
  2594. langid = 1033; // bloody cultural imperialists.
  2595. if (!GetNewSpellerEngine(langid, rgchEngine, cchEngine, rgchLex, cchLex))
  2596. {
  2597. return FALSE;
  2598. }
  2599. }
  2600. }
  2601. else
  2602. return TRUE;
  2603. }
  2604. Assert(rgchEngine[0]); // should be something in the engine name!
  2605. m_hinstDll = LoadLibrary(rgchEngine);
  2606. if (!m_hinstDll)
  2607. {
  2608. m_pfnSpellerCloseLex = 0;
  2609. m_pfnSpellerTerminate = 0;
  2610. return FALSE;
  2611. }
  2612. // We are not using csapi3t1.dll, so we should free it
  2613. m_fCSAPI3T1 = FALSE;
  2614. GetAddr(m_pfnSpellerVersion, PROOFVERSION, "SpellerVersion");
  2615. GetAddr(m_pfnSpellerInit, PROOFINIT, "SpellerInit");
  2616. GetAddr(m_pfnSpellerTerminate, PROOFTERMINATE, "SpellerTerminate");
  2617. GetAddr(m_pfnSpellerSetOptions, PROOFSETOPTIONS,"SpellerSetOptions");
  2618. GetAddr(m_pfnSpellerOpenLex, PROOFOPENLEX, "SpellerOpenLex");
  2619. GetAddr(m_pfnSpellerCloseLex, PROOFCLOSELEX, "SpellerCloseLex");
  2620. GetAddr(m_pfnSpellerCheck, SPELLERCHECK, "SpellerCheck");
  2621. GetAddr(m_pfnSpellerAddUdr, SPELLERADDUDR, "SpellerAddUdr");
  2622. GetAddr(m_pfnSpellerBuiltInUdr, SPELLERBUILTINUDR, "SpellerBuiltinUdr");
  2623. GetAddr(m_pfnSpellerAddChangeUdr, SPELLERADDCHANGEUDR, "SpellerAddChangeUdr");
  2624. params.versionAPI = PROOFTHISAPIVERSION;
  2625. if (m_pfnSpellerInit(&m_pid, &params) != ptecNoErrors)
  2626. return FALSE;
  2627. if (m_pfnSpellerSetOptions(m_pid, soselBits,
  2628. sobitSuggestFromUserLex | sobitIgnoreAllCaps | sobitIgnoreSingleLetter) != ptecNoErrors)
  2629. return FALSE;
  2630. m_langid = langid;
  2631. // Hebrew does not have a main lex
  2632. if ((langid != lidHebrew) || !m_fCSAPI3T1)
  2633. {
  2634. PROOFLEXIN plxin;
  2635. PROOFLEXOUT plxout;
  2636. WCHAR rgchLexW[MAX_PATH]={0};
  2637. // Tell the speller the name of the dictionary. This requires unicode conversion.
  2638. MultiByteToWideChar(CP_ACP, 0, rgchLex, -1, rgchLexW, ARRAYSIZE(rgchLexW)-1);
  2639. // open the main dict
  2640. plxin.pwszLex = rgchLexW;
  2641. plxin.fCreate = FALSE;
  2642. plxin.lxt = lxtMain;
  2643. plxin.lidExpected = langid;
  2644. memset(&plxout, 0, sizeof(plxout));
  2645. if (m_pfnSpellerOpenLex(m_pid, &plxin, &plxout) != ptecNoErrors)
  2646. return FALSE;
  2647. m_rgprflex[0] = plxout.lex;
  2648. m_clex++;
  2649. }
  2650. return TRUE;
  2651. // needed by the GetAddr macro -- bite me!!!!!!
  2652. error:
  2653. return FALSE;
  2654. }
  2655. BOOL EnumUserDictCallback(DWORD_PTR dwCookie, LPTSTR lpszDict)
  2656. {
  2657. CSpell *pSpell = (CSpell*)dwCookie;
  2658. Assert(pSpell);
  2659. return pSpell->OpenUserDictionary(lpszDict);
  2660. }
  2661. BOOL CSpell::OpenUserDictionary(LPTSTR lpszDict)
  2662. {
  2663. PROOFLEXIN plxin;
  2664. PROOFLEXOUT plxout;
  2665. WCHAR rgchUserDictW[MAX_PATH]={0};
  2666. // make sure our directory exists
  2667. {
  2668. TCHAR rgchDictDir[MAX_PATH];
  2669. lstrcpy(rgchDictDir, lpszDict);
  2670. PathRemoveFileSpec(rgchDictDir);
  2671. OpenDirectory(rgchDictDir);
  2672. }
  2673. MultiByteToWideChar(CP_ACP, 0, lpszDict, -1, rgchUserDictW, ARRAYSIZE(rgchUserDictW)-1);
  2674. plxin.pwszLex = rgchUserDictW;
  2675. plxin.fCreate = TRUE;
  2676. plxin.lxt = lxtUser;
  2677. plxin.lidExpected = m_langid;
  2678. memset(&plxout, 0, sizeof(plxout));
  2679. if ( m_pfnSpellerOpenLex(m_pid, &plxin, &plxout) != ptecNoErrors)
  2680. return TRUE;
  2681. m_rgprflex[m_clex++] = plxout.lex;
  2682. return TRUE;
  2683. }
  2684. BOOL CSpell::OpenUserDictionaries()
  2685. {
  2686. // now open the user dicts
  2687. EnumUserDictionaries((DWORD_PTR)this, EnumUserDictCallback);
  2688. // if only one dict open then we need to create default user dict
  2689. if (m_clex == 1)
  2690. {
  2691. PROOFLEXIN plxin;
  2692. PROOFLEXOUT plxout;
  2693. TCHAR rgchUserDict[MAX_PATH]={0};
  2694. if (GetDefaultUserDictionary(rgchUserDict, ARRAYSIZE(rgchUserDict)))
  2695. {
  2696. WCHAR rgchUserDictW[MAX_PATH];
  2697. // make sure our directory exists
  2698. {
  2699. TCHAR rgchDictDir[MAX_PATH];
  2700. lstrcpy(rgchDictDir, rgchUserDict);
  2701. PathRemoveFileSpec(rgchDictDir);
  2702. OpenDirectory(rgchDictDir);
  2703. }
  2704. MultiByteToWideChar(CP_ACP, 0, rgchUserDict, -1, rgchUserDictW, ARRAYSIZE(rgchUserDictW)-1);
  2705. plxin.pwszLex = rgchUserDictW;
  2706. plxin.fCreate = TRUE;
  2707. plxin.lxt = lxtUser;
  2708. plxin.lidExpected = m_langid;
  2709. memset(&plxout, 0, sizeof(plxout));
  2710. if (m_pfnSpellerOpenLex(m_pid, &plxin, &plxout) != ptecNoErrors)
  2711. return TRUE;
  2712. m_rgprflex[m_clex++] = plxout.lex;
  2713. }
  2714. }
  2715. return TRUE;
  2716. }
  2717. VOID CSpell::CloseSpeller()
  2718. {
  2719. SafeRelease(m_pDoc);
  2720. SafeRelease(m_pParentCmdTarget);
  2721. if (m_pfnSpellerCloseLex)
  2722. {
  2723. for(int i=0; i<cchMaxDicts; i++)
  2724. {
  2725. if (m_rgprflex[i])
  2726. {
  2727. m_pfnSpellerCloseLex(m_pid, m_rgprflex[i], TRUE);
  2728. m_rgprflex[i] = NULL;
  2729. }
  2730. }
  2731. }
  2732. if (m_pfnSpellerTerminate)
  2733. m_pfnSpellerTerminate(m_pid, TRUE);
  2734. m_pid = 0;
  2735. m_pfnSpellerVersion = 0;
  2736. m_pfnSpellerInit = 0;
  2737. m_pfnSpellerTerminate = 0;
  2738. m_pfnSpellerSetOptions = 0;
  2739. m_pfnSpellerOpenLex = 0;
  2740. m_pfnSpellerCloseLex = 0;
  2741. m_pfnSpellerCheck = 0;
  2742. m_pfnSpellerAddUdr = 0;
  2743. m_pfnSpellerAddChangeUdr= 0;
  2744. m_pfnSpellerBuiltInUdr = 0;
  2745. // As long as we are not using the global CSAPI3T1.DLL, free it
  2746. if (m_hinstDll && !m_fCSAPI3T1)
  2747. {
  2748. FreeLibrary(m_hinstDll);
  2749. m_hinstDll = NULL;
  2750. }
  2751. }
  2752. BOOL CSpell::GetNewSpellerEngine(LANGID lgid, TCHAR *rgchEngine, DWORD cchEngine, TCHAR *rgchLex, DWORD cchLex)
  2753. {
  2754. DWORD er;
  2755. LPCSTR rgpszDictionaryTypes[] = {"Normal", "Consise", "Complete"};
  2756. int cDictTypes = sizeof(rgpszDictionaryTypes) / sizeof(LPCSTR);
  2757. int i;
  2758. TCHAR rgchQual[MAX_PATH];
  2759. DWORD cch;
  2760. if (rgchEngine == NULL || rgchLex == NULL)
  2761. return FALSE;
  2762. *rgchEngine = 0;
  2763. *rgchLex = 0;
  2764. wsprintf(rgchQual, "%d\\Normal", lgid);
  2765. cch = cchEngine;
  2766. #ifdef DEBUG
  2767. er = MsiProvideQualifiedComponent(SPELLER_DEBUG_GUID, rgchQual, INSTALLMODE_DEFAULT, rgchEngine, &cch);
  2768. if (er != ERROR_SUCCESS)
  2769. {
  2770. cch = cchEngine;
  2771. er = MsiProvideQualifiedComponent(SPELLER_GUID, rgchQual, INSTALLMODE_DEFAULT, rgchEngine, &cch);
  2772. }
  2773. #else
  2774. er = MsiProvideQualifiedComponent(SPELLER_GUID, rgchQual, INSTALLMODE_DEFAULT, rgchEngine, &cch);
  2775. #endif
  2776. if (er != ERROR_SUCCESS)
  2777. return FALSE;
  2778. bool fFound = FALSE;
  2779. // Hebrew does not have a lex
  2780. if ((lgid != lidHebrew) || !m_fCSAPI3T1)
  2781. {
  2782. for (i = 0; i < cDictTypes; i++)
  2783. {
  2784. wsprintf(rgchQual, "%d\\%s", lgid, rgpszDictionaryTypes[i]);
  2785. cch = cchLex;
  2786. #ifdef DEBUG
  2787. er = MsiProvideQualifiedComponent(DICTIONARY_DEBUG_GUID, rgchQual, INSTALLMODE_DEFAULT, rgchLex, &cch);
  2788. if (er != ERROR_SUCCESS)
  2789. {
  2790. cch = cchLex;
  2791. er = MsiProvideQualifiedComponent(DICTIONARY_GUID, rgchQual, INSTALLMODE_DEFAULT, rgchLex, &cch);
  2792. }
  2793. #else // DEBUG
  2794. er = MsiProvideQualifiedComponent(DICTIONARY_GUID, rgchQual, INSTALLMODE_DEFAULT, rgchLex, &cch);
  2795. #endif // DEBUG
  2796. if (ERROR_SUCCESS == er)
  2797. {
  2798. fFound = TRUE;
  2799. break;
  2800. }
  2801. }
  2802. }
  2803. return fFound;
  2804. }
  2805. BOOL GetDefaultUserDictionary(TCHAR *rgchUserDict, int cchBuff)
  2806. {
  2807. DWORD dwType;
  2808. DWORD cbUserDict;
  2809. HKEY hkey = NULL;
  2810. BOOL fFound = FALSE;
  2811. LPTSTR pszEnd;
  2812. if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegSharedTools, 0, KEY_QUERY_VALUE, &hkey))
  2813. {
  2814. cbUserDict = cchBuff * sizeof(TCHAR);
  2815. if (SHQueryValueEx(hkey, c_szRegSharedToolsPath, 0L, &dwType, rgchUserDict, &cbUserDict) == ERROR_SUCCESS)
  2816. {
  2817. pszEnd = PathAddBackslash(rgchUserDict);
  2818. if (pszEnd)
  2819. {
  2820. lstrcpy(pszEnd, c_szRegDefCustomDict);
  2821. fFound = TRUE;
  2822. }
  2823. }
  2824. RegCloseKey(hkey);
  2825. }
  2826. // if we where able to create a path to the user dict store it in the regdb
  2827. if (fFound)
  2828. {
  2829. TCHAR rgchBuf[cchMaxPathName];
  2830. lstrcpy(rgchBuf, c_szRegSpellProfile);
  2831. lstrcat(rgchBuf, c_szRegSpellKeyCustom);
  2832. if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, rgchBuf, 0, rgchBuf, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &hkey, NULL) == ERROR_SUCCESS)
  2833. {
  2834. RegSetValueEx(hkey, c_szRegSpellPathDict, 0, REG_SZ, (BYTE *)rgchUserDict, (lstrlen(rgchUserDict) + 1) * sizeof(TCHAR));
  2835. RegCloseKey(hkey);
  2836. }
  2837. }
  2838. return fFound;
  2839. }
  2840. VOID CSpell::DeInitRanges()
  2841. {
  2842. VARIANT_BOOL fSuccess;
  2843. if(m_pRangeSel)
  2844. m_pRangeSel->select();
  2845. SafeRelease(m_pRangeDocStartSelStart);
  2846. SafeRelease(m_pRangeSel);
  2847. SafeRelease(m_pRangeSelExpand);
  2848. SafeRelease(m_pRangeSelEndDocEnd);
  2849. SafeRelease(m_pRangeChecking);
  2850. SafeRelease(m_pRangeUndoSave);
  2851. SafeRelease(m_pBodyElem);
  2852. SafeRelease(m_pMarkup);
  2853. m_hwndDlg = NULL;
  2854. }
  2855. HRESULT CSpell::HrInitRanges(IHTMLTxtRange *pRangeIgnore, HWND hwndMain, BOOL fSuppressDoneMsg)
  2856. {
  2857. HRESULT hr = NOERROR;
  2858. IDispatch* pID=0;
  2859. VARIANT_BOOL fSuccess;
  2860. IHTMLTxtRange* pRangeDoc = NULL;
  2861. IHTMLSelectionObject* pSel = NULL;
  2862. BSTR bstr = NULL;
  2863. IMarkupPointer *pRangeStart = NULL;
  2864. IMarkupPointer *pRangeEnd = NULL;
  2865. IMarkupPointer *pRangeTemp = NULL;
  2866. MARKUP_CONTEXT_TYPE markupContext;
  2867. long cch;
  2868. OLECHAR chText[64];
  2869. BOOL fResult;
  2870. Assert(m_pDoc);
  2871. m_hwndNote = hwndMain;
  2872. m_fShowDoneMsg = !fSuppressDoneMsg;
  2873. m_pRangeIgnore = pRangeIgnore;
  2874. hr = _EnsureInited();
  2875. if (FAILED(hr))
  2876. goto error;
  2877. m_pBodyElem->createTextRange(&pRangeDoc);
  2878. if(!pRangeDoc)
  2879. {
  2880. hr = E_FAIL;
  2881. goto error;
  2882. }
  2883. m_pDoc->get_selection(&pSel);
  2884. if(!pSel)
  2885. {
  2886. hr = E_FAIL;
  2887. goto error;
  2888. }
  2889. pSel->createRange(&pID);
  2890. if(!pID)
  2891. {
  2892. hr = E_FAIL;
  2893. goto error;
  2894. }
  2895. pID->QueryInterface(IID_IHTMLTxtRange, (LPVOID *)&m_pRangeSel);
  2896. if(!m_pRangeSel)
  2897. {
  2898. // if the selection is on an image or something rather than text, it fails.
  2899. // So we just start spellchecking from the beginning.
  2900. pRangeDoc->duplicate(&m_pRangeSel);
  2901. if(!m_pRangeSel)
  2902. {
  2903. hr = E_FAIL;
  2904. goto error;
  2905. }
  2906. hr = m_pRangeSel->collapse(VARIANT_TRUE);
  2907. if(FAILED(hr))
  2908. goto error;
  2909. }
  2910. Assert(m_pRangeSel);
  2911. m_pRangeSel->duplicate(&m_pRangeSelExpand);
  2912. if(!m_pRangeSelExpand)
  2913. {
  2914. hr = E_FAIL;
  2915. goto error;
  2916. }
  2917. hr = m_pRangeSelExpand->expand((BSTR)c_bstr_Word, &fSuccess);
  2918. if(FAILED(hr))
  2919. goto error;
  2920. hr = m_pRangeSel->get_text(&bstr);
  2921. if(FAILED(hr))
  2922. goto error;
  2923. if(!bstr || lstrlenW(bstr) == 0)
  2924. {
  2925. m_State = SELENDDOCEND;
  2926. hr = m_pRangeSelExpand->collapse(VARIANT_TRUE);
  2927. if(FAILED(hr))
  2928. goto error;
  2929. }
  2930. else
  2931. m_State = SEL;
  2932. // make sure we backup over any abbreviations
  2933. // it would be nice if Trident could do this!
  2934. {
  2935. hr = m_pMarkup->CreateMarkupPointer(&pRangeStart);
  2936. if (FAILED(hr))
  2937. goto error;
  2938. hr = m_pMarkup->CreateMarkupPointer(&pRangeEnd);
  2939. if (FAILED(hr))
  2940. goto error;
  2941. hr = m_pMarkup->CreateMarkupPointer(&pRangeTemp);
  2942. if (FAILED(hr))
  2943. goto error;
  2944. hr = m_pMarkup->MovePointersToRange(m_pRangeSelExpand, pRangeStart, pRangeEnd);
  2945. if (FAILED(hr))
  2946. goto error;
  2947. // first check to see if we have a character to the right or a '.'
  2948. // if not it's not an abbreviation
  2949. {
  2950. hr = pRangeTemp->MoveToPointer(pRangeStart);
  2951. if (FAILED(hr))
  2952. goto error;
  2953. while(TRUE)
  2954. {
  2955. cch = 1;
  2956. hr = pRangeTemp->Right(FALSE, &markupContext, NULL, &cch, chText);
  2957. if (FAILED(hr))
  2958. goto error;
  2959. if (markupContext == CONTEXT_TYPE_None)
  2960. goto noAbbreviation;
  2961. if (markupContext == CONTEXT_TYPE_Text)
  2962. {
  2963. WORD wType;
  2964. wType = GetWCharType(chText[0]);
  2965. if ((C1_SPACE & wType) || ((C1_PUNCT & wType) && chText[0] != L'.'))
  2966. goto noAbbreviation;
  2967. }
  2968. cch = 1;
  2969. hr = pRangeTemp->Right(TRUE, NULL, NULL, &cch, NULL);
  2970. if (FAILED(hr))
  2971. goto error;
  2972. if (markupContext == CONTEXT_TYPE_Text)
  2973. {
  2974. hr = HrHasWhitespace(pRangeStart, pRangeTemp, &fResult);
  2975. if (FAILED(hr))
  2976. goto error;
  2977. if (fResult)
  2978. goto noAbbreviation;
  2979. break;
  2980. }
  2981. }
  2982. }
  2983. // now look for a period
  2984. {
  2985. processNextWord:
  2986. hr = pRangeEnd->MoveToPointer(pRangeStart);
  2987. if (FAILED(hr))
  2988. goto error;
  2989. hr = pRangeTemp->MoveToPointer(pRangeStart);
  2990. if (FAILED(hr))
  2991. goto error;
  2992. while(TRUE)
  2993. {
  2994. cch = 1;
  2995. hr = pRangeTemp->Left(FALSE, &markupContext, NULL, &cch, chText);
  2996. if (FAILED(hr))
  2997. goto error;
  2998. if (markupContext == CONTEXT_TYPE_None)
  2999. goto finishedAbbreviation;
  3000. if (markupContext == CONTEXT_TYPE_Text)
  3001. {
  3002. WORD wType;
  3003. wType = GetWCharType(chText[0]);
  3004. if ((C1_SPACE & wType) || ((C1_PUNCT & wType) && chText[0] != L'.'))
  3005. goto finishedAbbreviation;
  3006. }
  3007. cch = 1;
  3008. hr = pRangeTemp->Left(TRUE, NULL, NULL, &cch, NULL);
  3009. if (FAILED(hr))
  3010. goto error;
  3011. if (markupContext == CONTEXT_TYPE_Text && chText[0] == L'.')
  3012. {
  3013. hr = pRangeTemp->MoveUnit(MOVEUNIT_PREVWORDBEGIN);
  3014. if (FAILED(hr))
  3015. goto finishedAbbreviation;
  3016. hr = HrHasWhitespace(pRangeTemp, pRangeEnd, &fResult);
  3017. if (FAILED(hr))
  3018. goto error;
  3019. if (fResult)
  3020. goto finishedAbbreviation;
  3021. pRangeStart->MoveToPointer(pRangeTemp);
  3022. if (FAILED(hr))
  3023. goto error;
  3024. goto processNextWord;
  3025. }
  3026. }
  3027. }
  3028. finishedAbbreviation:
  3029. hr = m_pMarkup->MovePointersToRange(m_pRangeSelExpand, pRangeTemp, pRangeEnd);
  3030. if (FAILED(hr))
  3031. goto error;
  3032. // check to see if we had a selection
  3033. // if not be sure to set new selection correctly
  3034. hr = pRangeTemp->IsEqualTo(pRangeEnd, &fResult);
  3035. if (FAILED(hr))
  3036. goto error;
  3037. hr = m_pMarkup->MoveRangeToPointers(pRangeStart, fResult ? pRangeStart : pRangeEnd, m_pRangeSelExpand);
  3038. if (FAILED(hr))
  3039. goto error;
  3040. noAbbreviation:
  3041. ;
  3042. }
  3043. m_pBodyElem->createTextRange(&m_pRangeSelEndDocEnd);
  3044. if(!m_pRangeSelEndDocEnd)
  3045. {
  3046. hr = E_FAIL;
  3047. goto error;
  3048. }
  3049. m_pRangeSelEndDocEnd->duplicate(&m_pRangeDocStartSelStart);
  3050. if(!m_pRangeDocStartSelStart)
  3051. {
  3052. hr = E_FAIL;
  3053. goto error;
  3054. }
  3055. hr = m_pRangeSelEndDocEnd->setEndPoint((BSTR)c_bstr_StartToEnd, m_pRangeSelExpand);
  3056. if(FAILED(hr))
  3057. goto error;
  3058. hr = m_pRangeSelEndDocEnd->setEndPoint((BSTR)c_bstr_EndToEnd, pRangeDoc);
  3059. if(FAILED(hr))
  3060. goto error;
  3061. hr = m_pRangeDocStartSelStart->setEndPoint((BSTR)c_bstr_StartToStart, pRangeDoc);
  3062. if(FAILED(hr))
  3063. goto error;
  3064. hr = m_pRangeDocStartSelStart->setEndPoint((BSTR)c_bstr_EndToStart, m_pRangeSelExpand);
  3065. if(FAILED(hr))
  3066. goto error;
  3067. error:
  3068. ReleaseObj(pRangeDoc);
  3069. ReleaseObj(pID);
  3070. ReleaseObj(pSel);
  3071. SafeSysFreeString(bstr);
  3072. SafeRelease(pRangeStart);
  3073. SafeRelease(pRangeEnd);
  3074. SafeRelease(pRangeTemp);
  3075. return hr;
  3076. }
  3077. HRESULT CSpell::HrReplaceSel(LPSTR szWord)
  3078. {
  3079. HRESULT hr = NOERROR;
  3080. BSTR bstrGet=0, bstrPut=0;
  3081. INT cch;
  3082. TCHAR szBuf[cchEditBufferMax]={0};
  3083. UINT uCodePage;
  3084. LPSTR psz;
  3085. BOOL fSquiggle=FALSE;
  3086. LONG cb = 0;
  3087. if(!m_pRangeChecking || szWord==NULL)
  3088. return E_INVALIDARG;
  3089. if (*szWord == 0)
  3090. {
  3091. hr = m_pRangeChecking->moveStart((BSTR)c_bstr_Character, -1, &cb);
  3092. //If we failed to movestart, we just delete the word.
  3093. hr = m_pRangeChecking->put_text(L"");
  3094. goto error;
  3095. }
  3096. #ifdef BACKGROUNDSPELL
  3097. if (HrHasSquiggle(m_pRangeChecking)==S_OK)
  3098. fSquiggle = TRUE;
  3099. #endif // BACKGROUNDSPELL
  3100. hr = m_pRangeChecking->get_text(&bstrGet);
  3101. if(!bstrGet || lstrlenW(bstrGet)==0)
  3102. goto error;
  3103. uCodePage = GetCodePage();
  3104. cch = SysStringLen(bstrGet);
  3105. if (!WideCharToMultiByte(uCodePage, 0, bstrGet, -1, szBuf, sizeof(szBuf), NULL, NULL))
  3106. {
  3107. hr = E_FAIL;
  3108. goto error;
  3109. }
  3110. psz = StrChr(szBuf, ' ');
  3111. if(psz)
  3112. {
  3113. TCHAR szPut[cchEditBufferMax]={0};
  3114. wsprintf(szPut, c_szFmt, szWord, psz);
  3115. hr = HrLPSZToBSTR(szPut, &bstrPut);
  3116. }
  3117. else
  3118. hr = HrLPSZToBSTR(szWord, &bstrPut);
  3119. if (FAILED(hr))
  3120. goto error;
  3121. if (!fSquiggle)
  3122. hr = m_pRangeChecking->put_text(bstrPut);
  3123. else
  3124. hr = m_pRangeChecking->pasteHTML(bstrPut);
  3125. if(FAILED(hr))
  3126. goto error;
  3127. error:
  3128. if (SUCCEEDED(hr))
  3129. hr = HrUpdateSelection();
  3130. SysFreeString(bstrGet);
  3131. SysFreeString(bstrPut);
  3132. return hr;
  3133. }
  3134. HRESULT CSpell::GetSelection(IHTMLTxtRange **ppRange)
  3135. {
  3136. IHTMLSelectionObject* pSel = NULL;
  3137. IHTMLTxtRange *pTxtRange=0;
  3138. IDispatch *pID=0;
  3139. HRESULT hr=E_FAIL;
  3140. if (ppRange == NULL)
  3141. return TraceResult(E_INVALIDARG);
  3142. *ppRange = NULL;
  3143. if(m_pDoc)
  3144. {
  3145. m_pDoc->get_selection(&pSel);
  3146. if (pSel)
  3147. {
  3148. pSel->createRange(&pID);
  3149. if (pID)
  3150. {
  3151. hr = pID->QueryInterface(IID_IHTMLTxtRange, (LPVOID *)ppRange);
  3152. pID->Release();
  3153. }
  3154. pSel->Release();
  3155. }
  3156. }
  3157. return hr;
  3158. }
  3159. #ifdef BACKGROUNDSPELL
  3160. HRESULT CSpell::HrRegisterKeyPressNotify(BOOL fRegister)
  3161. {
  3162. IConnectionPointContainer * pCPContainer=0;
  3163. IConnectionPoint * pCP=0;
  3164. HRESULT hr;
  3165. Assert(m_pDoc)
  3166. hr = m_pDoc->QueryInterface(IID_IConnectionPointContainer, (LPVOID *)&pCPContainer);
  3167. if (FAILED(hr))
  3168. goto error;
  3169. hr = pCPContainer->FindConnectionPoint(DIID_HTMLDocumentEvents, &pCP);
  3170. pCPContainer->Release();
  3171. if (FAILED(hr))
  3172. goto error;
  3173. if (fRegister)
  3174. {
  3175. Assert(0==m_dwCookieNotify);
  3176. hr = pCP->Advise(this, &m_dwCookieNotify);
  3177. if (FAILED(hr))
  3178. goto error;
  3179. }
  3180. else
  3181. {
  3182. if (m_dwCookieNotify)
  3183. {
  3184. hr = pCP->Unadvise(m_dwCookieNotify);
  3185. if (FAILED(hr))
  3186. goto error;
  3187. }
  3188. }
  3189. error:
  3190. ReleaseObj(pCP);
  3191. return hr;
  3192. }
  3193. #endif // BACKGROUNDSPELL
  3194. HRESULT CSpell::OnWMCommand(int id, IHTMLTxtRange *pTxtRange)
  3195. {
  3196. switch (id)
  3197. {
  3198. case idmSuggest0:
  3199. case idmSuggest1:
  3200. case idmSuggest2:
  3201. case idmSuggest3:
  3202. case idmSuggest4:
  3203. HrReplaceBySuggest(pTxtRange, id-idmSuggest0);
  3204. break;
  3205. case idmIgnore:
  3206. case idmIgnoreAll:
  3207. case idmAdd:
  3208. #ifdef BACKGROUNDSPELL
  3209. HrDeleteSquiggle(pTxtRange);
  3210. #endif // BACKGROUNDSPELL
  3211. break;
  3212. default:
  3213. return S_FALSE;
  3214. }
  3215. return S_OK;
  3216. }
  3217. HRESULT CSpell::HrUpdateSelection()
  3218. {
  3219. HRESULT hr;
  3220. VARIANT_BOOL fSuccess;
  3221. SafeRelease(m_pRangeSel);
  3222. m_pRangeSelEndDocEnd->duplicate(&m_pRangeSel);
  3223. if (!m_pRangeSel)
  3224. {
  3225. hr = E_FAIL;
  3226. goto error;
  3227. }
  3228. hr = m_pRangeSel->setEndPoint((BSTR)c_bstr_EndToStart, m_pRangeSelEndDocEnd);
  3229. if (FAILED(hr))
  3230. goto error;
  3231. hr = m_pRangeSel->setEndPoint((BSTR)c_bstr_StartToEnd, m_pRangeDocStartSelStart);
  3232. if (FAILED(hr))
  3233. goto error;
  3234. SafeRelease(m_pRangeSelExpand);
  3235. m_pRangeSel->duplicate(&m_pRangeSelExpand);
  3236. if(!m_pRangeSelExpand)
  3237. {
  3238. hr = E_FAIL;
  3239. goto error;
  3240. }
  3241. hr = m_pRangeSelExpand->expand((BSTR)c_bstr_Word, &fSuccess);
  3242. if(FAILED(hr))
  3243. goto error;
  3244. error:
  3245. return hr;
  3246. }
  3247. BOOL CSpell::FIgnoreNumber()
  3248. {
  3249. return (m_dwOpt & MESPELLOPT_IGNORENUMBER);
  3250. }
  3251. BOOL CSpell::FIgnoreUpper()
  3252. {
  3253. return (m_dwOpt & MESPELLOPT_IGNOREUPPER);
  3254. }
  3255. BOOL CSpell::FIgnoreDBCS()
  3256. {
  3257. return (m_dwOpt & MESPELLOPT_IGNOREDBCS);
  3258. }
  3259. BOOL CSpell::FIgnoreProtect()
  3260. {
  3261. return (m_dwOpt & MESPELLOPT_IGNOREPROTECT);
  3262. }
  3263. BOOL CSpell::FAlwaysSuggest()
  3264. {
  3265. return (m_dwOpt & MESPELLOPT_ALWAYSSUGGEST);
  3266. }
  3267. BOOL CSpell::FCheckOnSend()
  3268. {
  3269. return (m_dwOpt & MESPELLOPT_CHECKONSEND);
  3270. }
  3271. BOOL CSpell::FIgnoreURL()
  3272. {
  3273. return (m_dwOpt & MESPELLOPT_IGNOREURL);
  3274. }
  3275. UINT CSpell::GetCodePage()
  3276. {
  3277. UINT uCodePage;
  3278. TCHAR szBuf[cchEditBufferMax]={0};
  3279. if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE, szBuf, sizeof(szBuf)))
  3280. uCodePage = StrToInt(szBuf);
  3281. else
  3282. uCodePage = CP_ACP;
  3283. return uCodePage;
  3284. }
  3285. void DumpRange(IHTMLTxtRange *pRange)
  3286. {
  3287. #ifdef DEBUG
  3288. BSTR bstrGet=0;
  3289. if (!pRange)
  3290. return;
  3291. pRange->get_text(&bstrGet);
  3292. SysFreeString(bstrGet);
  3293. #endif
  3294. }
  3295. BOOL FBadSpellChecker(LPSTR rgchBufDigit)
  3296. {
  3297. TCHAR rgchBufKey[cchMaxPathName];
  3298. TCHAR rgchBuf[cchMaxPathName];
  3299. TCHAR szMdr[cchMaxPathName];
  3300. LPSTR pszSpell;
  3301. wsprintf(rgchBufKey, c_szRegSpellKeyDef, rgchBufDigit);
  3302. if (!GetSpellingPaths(rgchBufKey, rgchBuf, szMdr, sizeof(rgchBuf)/sizeof(TCHAR)))
  3303. return TRUE;
  3304. pszSpell = PathFindFileNameA(rgchBuf);
  3305. if (!pszSpell)
  3306. return TRUE;
  3307. if (lstrcmpi(pszSpell, "msspell.dll")==0 ||
  3308. lstrcmpi(pszSpell, "mssp32.dll")==0)
  3309. return TRUE;
  3310. // [email protected] - check that the dict exists (also check the spell dll
  3311. // for good measure) - 42208
  3312. // spell dll must exist
  3313. if (!PathFileExists(rgchBuf))
  3314. return TRUE;
  3315. // main dict must exist
  3316. if (!PathFileExists(szMdr))
  3317. return TRUE;
  3318. return FALSE;
  3319. }
  3320. #ifdef BACKGROUNDSPELL
  3321. CSpellStack::CSpellStack()
  3322. {
  3323. m_cRef = 1;
  3324. m_sp = -1;
  3325. ZeroMemory(&m_rgStack, sizeof(CCell)*MAX_SPELLSTACK);
  3326. }
  3327. CSpellStack::~CSpellStack()
  3328. {
  3329. while (m_sp>=0)
  3330. {
  3331. SafeRelease(m_rgStack[m_sp].pTextRange);
  3332. m_sp--;
  3333. }
  3334. }
  3335. ULONG CSpellStack::AddRef()
  3336. {
  3337. return ++m_cRef;
  3338. }
  3339. ULONG CSpellStack::Release()
  3340. {
  3341. if (--m_cRef==0)
  3342. {
  3343. delete this;
  3344. return 0;
  3345. }
  3346. return m_cRef;
  3347. }
  3348. HRESULT CSpellStack::HrGetRange(IHTMLTxtRange **ppTxtRange)
  3349. {
  3350. HRESULT hr;
  3351. Assert(ppTxtRange);
  3352. *ppTxtRange = 0;
  3353. if (m_sp < 0)
  3354. return E_FAIL;
  3355. *ppTxtRange = m_rgStack[m_sp].pTextRange;
  3356. if (*ppTxtRange)
  3357. (*ppTxtRange)->AddRef();
  3358. return NOERROR;
  3359. }
  3360. HRESULT CSpellStack::push(IHTMLTxtRange *pTxtRange)
  3361. {
  3362. HRESULT hr;
  3363. BSTR bstr=0;
  3364. Assert(m_sp >= -1 && m_sp <= (MAX_SPELLSTACK-2));
  3365. if (pTxtRange == NULL)
  3366. return E_INVALIDARG;
  3367. hr = pTxtRange->get_text(&bstr);
  3368. if (FAILED(hr) || bstr==NULL || *bstr==L'\0' || *bstr==L' ')
  3369. {
  3370. Assert(0);
  3371. goto error;
  3372. }
  3373. m_sp++;
  3374. m_rgStack[m_sp].pTextRange = pTxtRange;
  3375. pTxtRange->AddRef();
  3376. error:
  3377. SafeSysFreeString(bstr);
  3378. return NOERROR;
  3379. }
  3380. HRESULT CSpellStack::pop()
  3381. {
  3382. if (m_sp < 0)
  3383. return NOERROR;
  3384. Assert(m_sp>=0 && m_sp<=(MAX_SPELLSTACK-1));
  3385. SafeRelease(m_rgStack[m_sp].pTextRange);
  3386. m_sp--;
  3387. return NOERROR;
  3388. }
  3389. BOOL CSpellStack::fEmpty()
  3390. {
  3391. Assert(m_sp>=-1 && m_sp<=(MAX_SPELLSTACK-1));
  3392. if (m_sp < 0)
  3393. return TRUE;
  3394. else
  3395. return FALSE;
  3396. }
  3397. #endif // BACKGROUNDSPELL
  3398. WORD GetWCharType(WCHAR wc)
  3399. {
  3400. BOOL fResult;
  3401. WORD wResult;
  3402. fResult = GetStringTypeExWrapW(CP_ACP, CT_CTYPE1, &wc, 1, &wResult);
  3403. if (FALSE == fResult)
  3404. return 0;
  3405. else
  3406. return wResult;
  3407. }
  3408. /*******************************************************************
  3409. NAME: OpenDirectory
  3410. SYNOPSIS: checks for existence of directory, if it doesn't exist
  3411. it is created
  3412. ********************************************************************/
  3413. HRESULT OpenDirectory(TCHAR *szDir)
  3414. {
  3415. TCHAR *sz, ch;
  3416. HRESULT hr;
  3417. Assert(szDir != NULL);
  3418. hr = S_OK;
  3419. if (!CreateDirectory(szDir, NULL) && ERROR_ALREADY_EXISTS != GetLastError())
  3420. {
  3421. Assert(szDir[1] == _T(':'));
  3422. Assert(szDir[2] == _T('\\'));
  3423. sz = &szDir[3];
  3424. while (TRUE)
  3425. {
  3426. while (*sz != 0)
  3427. {
  3428. if (!IsDBCSLeadByte(*sz))
  3429. {
  3430. if (*sz == _T('\\'))
  3431. break;
  3432. }
  3433. sz = CharNext(sz);
  3434. }
  3435. ch = *sz;
  3436. *sz = 0;
  3437. if (!CreateDirectory(szDir, NULL))
  3438. {
  3439. if (GetLastError() != ERROR_ALREADY_EXISTS)
  3440. {
  3441. hr = E_FAIL;
  3442. *sz = ch;
  3443. break;
  3444. }
  3445. }
  3446. *sz = ch;
  3447. if (*sz == 0)
  3448. break;
  3449. sz++;
  3450. }
  3451. }
  3452. return(hr);
  3453. }
  3454. HRESULT CSpell::_EnsureInited()
  3455. {
  3456. HRESULT hr=S_OK;
  3457. if (m_pMarkup == NULL)
  3458. {
  3459. hr = m_pDoc->QueryInterface(IID_IMarkupServices, (LPVOID *)&m_pMarkup);
  3460. if (FAILED(hr))
  3461. goto error;
  3462. }
  3463. if (m_pBodyElem == NULL)
  3464. {
  3465. hr = HrGetBodyElement(m_pDoc, &m_pBodyElem);
  3466. if (FAILED(hr))
  3467. goto error;
  3468. }
  3469. error:
  3470. return hr;
  3471. }