Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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