Opera 12.15 Source Code
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.

OpDocumentEditSpellcheck.cpp 27KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  1. /* -*- Mode: c++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
  2. *
  3. * Copyright (C) 1995-2010 Opera Software AS. All rights reserved.
  4. *
  5. * This file is part of the Opera web browser.
  6. * It may not be distributed under any circumstances.
  7. */
  8. #include "core/pch.h"
  9. #if defined(DOCUMENT_EDIT_SUPPORT) && defined(INTERNAL_SPELLCHECK_SUPPORT)
  10. #include "modules/documentedit/OpDocumentEdit.h"
  11. #include "modules/documentedit/OpDocumentEditUtils.h"
  12. #include "modules/display/vis_dev.h"
  13. #include "modules/pi/OpSystemInfo.h"
  14. #include "modules/layout/layout_workplace.h"
  15. void OpDocumentEdit::DisableSpellcheck()
  16. {
  17. DisableSpellcheckInternal(TRUE /*by_user*/, TRUE /*force*/);
  18. }
  19. OpSpellCheckerWordIterator* OpDocumentEdit::GetAllContentIterator()
  20. {
  21. if(m_background_updater.Active())
  22. return NULL;
  23. HTML_Element *first = GetBody(), *last = NULL;
  24. if(first && first->Type() == HE_BODY)
  25. last = first->LastLeafActual();
  26. if(!first || !last)
  27. return NULL;
  28. m_background_updater.SetRange(first, last);
  29. if(!m_background_updater.Active())
  30. return NULL;
  31. return &m_background_updater;
  32. }
  33. BOOL OpDocumentEdit::SetSpellCheckLanguage(const uni_char *lang)
  34. {
  35. return OpStatus::IsSuccess(EnableSpellcheckInternal(TRUE /*by_user*/, lang));
  36. }
  37. BOOL OpDocumentEdit::HandleSpellCheckCommand(BOOL showui, const uni_char *value)
  38. {
  39. int spell_session_id = 0;
  40. CreateSpellUISession(NULL, spell_session_id);
  41. OpSpellUiSessionImpl session(spell_session_id);
  42. return session.SetLanguage(value);
  43. }
  44. void OpDocumentEdit::OnSessionReady(OpSpellCheckerSession *session)
  45. {
  46. OP_ASSERT(session == m_spell_session);
  47. HTML_Element *first = GetBody(), *last = NULL;
  48. if(first && first->Type() == HE_BODY)
  49. last = first->LastLeafActual();
  50. if(!first || !last)
  51. return;
  52. SpellCheckRange(first, last);
  53. }
  54. void OpDocumentEdit::OnError(OpSpellCheckerSession *session, OP_STATUS error_status, const uni_char *error_string)
  55. {
  56. OP_ASSERT(session == m_spell_session);
  57. DisableSpellcheckInternal(FALSE /*by_user*/, TRUE /*force*/);
  58. }
  59. void OpDocumentEdit::DisableSpellcheckInternal(BOOL by_user, BOOL force)
  60. {
  61. if(!force && !by_user && m_by_user)
  62. return;
  63. if(m_spell_session || by_user)
  64. m_by_user = by_user;
  65. if(!m_spell_session)
  66. return;
  67. m_word_iterator.Reset();
  68. m_replace_word.Reset();
  69. m_background_updater.Reset();
  70. OP_DELETE(m_spell_session);
  71. m_spell_session = NULL;
  72. if(m_has_spellchecked)
  73. {
  74. HTML_Element *root = GetRoot();
  75. HTML_Element *helm = root;
  76. while(helm)
  77. {
  78. if(helm->Type() == HE_TEXT && helm->GetLayoutBox() && helm->GetLayoutBox()->IsTextBox())
  79. {
  80. int i;
  81. Text_Box *box = (Text_Box*)helm->GetLayoutBox();
  82. WordInfo *words = box->GetWords();
  83. for(i=0;i<box->GetWordCount();i++)
  84. words[i].SetIsMisspelling(FALSE);
  85. }
  86. helm = helm->NextActual();
  87. }
  88. if(root)
  89. RepaintElement(root);
  90. }
  91. m_has_spellchecked = FALSE;
  92. m_last_helm_spelled = NULL;
  93. }
  94. OP_STATUS OpDocumentEdit::EnableSpellcheckInternal(BOOL by_user, const uni_char* lang)
  95. {
  96. if(!by_user && m_by_user)
  97. return OpStatus::OK;
  98. OP_ASSERT(!m_spell_session);
  99. OP_STATUS status = g_internal_spellcheck->CreateSession(lang, this, m_spell_session, m_blocking_spellcheck);
  100. if (OpStatus::IsError(status))
  101. {
  102. OP_ASSERT(m_spell_session == NULL);
  103. by_user = FALSE; // Remaining disabled is not what the user wanted.
  104. }
  105. m_by_user = by_user;
  106. return status;
  107. }
  108. OP_STATUS OpDocumentEdit::SpellCheckRange(HTML_Element *first, HTML_Element *last)
  109. {
  110. OP_ASSERT(!m_word_iterator.Active() && m_spell_session);
  111. OP_STATUS status = OpStatus::OK;
  112. m_word_iterator.SetRange(first, last);
  113. if(!m_word_iterator.Active())
  114. return status;
  115. if(OpStatus::IsError(status = m_spell_session->CheckText(&m_word_iterator, FALSE)))
  116. DisableSpellcheckInternal(FALSE /*by_user*/, TRUE /*force*/);
  117. return status;
  118. }
  119. void OpDocumentEdit::RepaintElement(HTML_Element *helm)
  120. {
  121. if(!helm || !helm->GetLayoutBox() || !m_doc || !m_doc->GetVisualDevice() || m_doc->IsReflowing() || m_doc->IsUndisplaying())
  122. return;
  123. if (m_doc->GetLogicalDocument() &&
  124. m_doc->GetLogicalDocument()->GetRoot() &&
  125. m_doc->GetLogicalDocument()->GetRoot()->IsAncestorOf(helm))
  126. {
  127. RECT box_rect;
  128. helm->GetLayoutBox()->GetRect(m_doc,CONTENT_BOX,box_rect);
  129. OpRect update_rect = OpRect(box_rect.left, box_rect.top, box_rect.right-box_rect.left, box_rect.bottom-box_rect.top);
  130. m_doc->GetVisualDevice()->Update(update_rect.x, update_rect.y, update_rect.width, update_rect.height);
  131. }
  132. }
  133. OP_STATUS OpDocumentEdit::MarkMisspellingInTextElement(BOOL misspelled, HTML_WordIterator *word, HTML_Element *helm, int start_ofs, int stop_ofs)
  134. {
  135. if(!helm->GetLayoutBox() || !helm->GetLayoutBox()->IsTextBox())
  136. return OpStatus::OK;
  137. Text_Box *box = (Text_Box*)helm->GetLayoutBox();
  138. if(!box->GetWords() || !box->GetWordCount())
  139. return OpStatus::OK;
  140. int i;
  141. WordInfo *words = box->GetWords();
  142. BOOL update = FALSE;
  143. BOOL spell_iterator = word == &m_word_iterator;
  144. int start_idx = 0;
  145. if(spell_iterator)
  146. {
  147. if(helm != m_last_helm_spelled)
  148. {
  149. if(m_last_helm_spelled)
  150. RepaintElement(m_last_helm_spelled);
  151. m_last_helm_spelled = helm;
  152. m_last_helm_spelled_needs_update = FALSE;
  153. m_next_wi_index_to_spell = 0;
  154. }
  155. start_idx = m_next_wi_index_to_spell;
  156. }
  157. int word_count = box->GetWordCount();
  158. for(i=start_idx;i<word_count;i++)
  159. {
  160. if((int) words[i].GetStart() >= start_ofs)
  161. {
  162. while(i < box->GetWordCount() && words[i].GetStart() < (UINT16)stop_ofs)
  163. {
  164. if(!words[i].IsMisspelling() != !misspelled)
  165. {
  166. update = TRUE;
  167. words[i].SetIsMisspelling(misspelled);
  168. }
  169. i++;
  170. }
  171. break;
  172. }
  173. }
  174. if(spell_iterator)
  175. {
  176. m_next_wi_index_to_spell = i;
  177. m_last_helm_spelled_needs_update = m_last_helm_spelled_needs_update || update;
  178. }
  179. else
  180. RepaintElement(helm);
  181. return OpStatus::OK;
  182. }
  183. OP_STATUS OpDocumentEdit::MarkNodesMisspelled(BOOL misspelled, HTML_WordIterator *word)
  184. {
  185. HTML_Element *start_elm = word->CurrentStartHelm();
  186. HTML_Element *stop_elm = word->CurrentStopHelm();
  187. int start_ofs = word->CurrentStartOfs();
  188. int stop_ofs = word->CurrentStopOfs();
  189. if(!start_elm)
  190. return OpStatus::OK;
  191. HTML_Element *helm = start_elm;
  192. while(helm)
  193. {
  194. if(helm->Type() == HE_TEXT && helm->GetTextContentLength())
  195. {
  196. int _stop_ofs = helm == stop_elm ? stop_ofs : helm->GetTextContentLength();
  197. RETURN_IF_ERROR(MarkMisspellingInTextElement(misspelled, word, helm, start_ofs, _stop_ofs));
  198. }
  199. start_ofs = 0;
  200. if(helm == stop_elm)
  201. break;
  202. helm = helm->NextActual();
  203. }
  204. return OpStatus::OK;
  205. }
  206. void OpDocumentEdit::OnCorrectSpellingFound(OpSpellCheckerSession *session, OpSpellCheckerWordIterator *word)
  207. {
  208. HTML_WordIterator *w = (HTML_WordIterator*)word;
  209. MarkNodesMisspelled(FALSE, w);
  210. }
  211. void OpDocumentEdit::OnMisspellingFound(OpSpellCheckerSession *session, OpSpellCheckerWordIterator *word, const uni_char **replacements)
  212. {
  213. m_has_spellchecked = TRUE;
  214. HTML_WordIterator *w = (HTML_WordIterator*)word;
  215. MarkNodesMisspelled(TRUE, w);
  216. }
  217. void OpDocumentEdit::RunPendingSpellCheck()
  218. {
  219. if(!m_spell_session || !m_pending_spell_first || m_word_iterator.Active())
  220. return;
  221. HTML_Element *first = m_pending_spell_first, *last = m_pending_spell_last;
  222. m_pending_spell_first = NULL;
  223. m_pending_spell_last = NULL;
  224. SpellCheckRange(first, last);
  225. }
  226. void OpDocumentEdit::OnSpellcheckerStopped()
  227. {
  228. if(m_last_helm_spelled && m_last_helm_spelled_needs_update && m_doc)
  229. RepaintElement(m_last_helm_spelled);
  230. m_last_helm_spelled = NULL;
  231. }
  232. void OpDocumentEdit::OnCheckingComplete(OpSpellCheckerSession *session)
  233. {
  234. if(m_last_helm_spelled && m_last_helm_spelled_needs_update)
  235. RepaintElement(m_last_helm_spelled);
  236. m_last_helm_spelled = NULL;
  237. OP_ASSERT(!m_word_iterator.Active());
  238. RunPendingSpellCheck();
  239. }
  240. void OpDocumentEdit::OnCheckingTakesABreak(OpSpellCheckerSession *session)
  241. {
  242. if(m_last_helm_spelled && m_last_helm_spelled_needs_update)
  243. RepaintElement(m_last_helm_spelled);
  244. m_last_helm_spelled = NULL;
  245. }
  246. void MoveSpellRangeOutOf(HTML_Element *parent, HTML_Element *&first, HTML_Element *&last)
  247. {
  248. if(!first)
  249. return;
  250. OP_ASSERT(last);
  251. BOOL first_ancestor = parent->IsAncestorOf(first);
  252. BOOL last_ancestor = parent->IsAncestorOf(last);
  253. if(!first_ancestor && !last_ancestor)
  254. return;
  255. if(first_ancestor && last_ancestor)
  256. {
  257. first = NULL;
  258. last = NULL;
  259. return;
  260. }
  261. if(first_ancestor)
  262. {
  263. first = (HTML_Element*)parent->NextSibling();
  264. while(first && first != last && !first->IsIncludedActual())
  265. first = first->Next();
  266. }
  267. else // last_ancestor
  268. {
  269. last = parent->Prev();
  270. while(last && last != first && !last->IsIncludedActual())
  271. last = last->Prev();
  272. }
  273. if(!first || !last)
  274. {
  275. OP_ASSERT(FALSE);
  276. first = NULL;
  277. last = NULL;
  278. }
  279. }
  280. void OpDocumentEdit::SpellInvalidateAround(HTML_Element *helm, BOOL must_be_outside_helm)
  281. {
  282. if(!m_spell_session || m_spell_session->GetState() == OpSpellCheckerSession::LOADING_DICTIONARY ||
  283. !GetRoot() || !GetRoot()->IsAncestorOf(helm))
  284. {
  285. return;
  286. }
  287. int i, len;
  288. const uni_char *content;
  289. HTML_Element *first = NULL, *last = NULL;
  290. HTML_Element *tmp = (HTML_Element*)helm->PrevSibling();
  291. while(tmp)
  292. {
  293. if(!IsFriendlyElement(tmp))
  294. break;
  295. if(tmp->IsIncludedActual() && tmp->Type() == HE_TEXT &&
  296. tmp->TextContent() && tmp->GetTextContentLength())
  297. {
  298. len = tmp->GetTextContentLength();
  299. content = tmp->TextContent();
  300. for(i=len-1;i >= 0 && !m_spell_session->IsWordSeparator(content[i]) ;i--) {}
  301. if(i != len-1) // tmp need spellcheck
  302. first = tmp;
  303. if(i >= 0)
  304. break;
  305. first = tmp;
  306. }
  307. tmp = tmp->LastChild() ? tmp->LastChild() : (HTML_Element*)tmp->PrevSibling();
  308. }
  309. tmp = (HTML_Element*)helm->NextSibling();
  310. while(tmp)
  311. {
  312. if(!IsFriendlyElement(tmp))
  313. break;
  314. if(tmp->IsIncludedActual() && tmp->Type() == HE_TEXT &&
  315. tmp->TextContent() && tmp->GetTextContentLength())
  316. {
  317. len = tmp->GetTextContentLength();
  318. content = tmp->TextContent();
  319. for(i=0;i < len && !m_spell_session->IsWordSeparator(content[i]) ;i++) {}
  320. if(i) // tmp need spellcheck
  321. last = tmp;
  322. if(i < len)
  323. break;
  324. last = tmp;
  325. }
  326. tmp = tmp->Next();
  327. }
  328. if(must_be_outside_helm)
  329. {
  330. MoveSpellRangeOutOf(helm, m_pending_spell_first, m_pending_spell_last);
  331. if(!first && !last)
  332. return;
  333. if(!first)
  334. {
  335. first = (HTML_Element*)helm->NextSibling();
  336. while(first && !first->IsIncludedActual())
  337. first = first->Next();
  338. if(!first)
  339. {
  340. OP_ASSERT(FALSE);
  341. return;
  342. }
  343. }
  344. if(!last)
  345. {
  346. last = helm->Prev();
  347. while(last && !last->IsIncludedActual())
  348. last = last->Prev();
  349. if(!last)
  350. {
  351. OP_ASSERT(FALSE);
  352. return;
  353. }
  354. }
  355. }
  356. else
  357. {
  358. if(!first)
  359. {
  360. first = helm;
  361. HTML_Element *stop = last ? last : (HTML_Element*)helm->NextSibling();
  362. while(first && first != stop && !first->IsIncludedActual())
  363. first = first->Next();
  364. if(first == stop && (stop != last || !last))
  365. return;
  366. }
  367. OP_ASSERT(first && first->IsIncludedActual());
  368. if(!last)
  369. {
  370. last = helm->LastLeaf();
  371. while(last && !last->IsIncludedActual())
  372. last = last->Prev();
  373. if(!last)
  374. {
  375. OP_ASSERT(FALSE);
  376. return;
  377. }
  378. }
  379. }
  380. if(!m_word_iterator.Active() && !m_begin_count)
  381. {
  382. SpellCheckRange(first, last);
  383. return;
  384. }
  385. else if (m_word_iterator.Active() && m_word_iterator.IsInRange(helm))
  386. {
  387. m_word_iterator.RestartFromBeginning();
  388. return;
  389. }
  390. if(!m_pending_spell_first)
  391. {
  392. m_pending_spell_first = first;
  393. m_pending_spell_last = last;
  394. return;
  395. }
  396. if(first->Precedes(m_pending_spell_first) || helm->IsAncestorOf(m_pending_spell_first))
  397. m_pending_spell_first = first;
  398. if(m_pending_spell_last->Precedes(last) || helm->IsAncestorOf(m_pending_spell_last))
  399. m_pending_spell_last = last;
  400. }
  401. OP_STATUS OpDocumentEdit::CreateSpellUISessionInternal(IntersectionObject *intersection, int &spell_session_id)
  402. {
  403. spell_session_id = 0;
  404. if(!m_spell_session || m_spell_session->GetState() == OpSpellCheckerSession::LOADING_DICTIONARY || !intersection)
  405. return OpStatus::OK;
  406. HTML_Element *helm = intersection->GetInnerBox() ? intersection->GetInnerBox()->GetHtmlElement() : NULL;
  407. if(!helm || helm->Type() != HE_TEXT || !intersection->GetWord())
  408. return OpStatus::OK;
  409. UINTPTR _ofs = (UINTPTR)(intersection->GetWord() - helm->TextContent());
  410. if(_ofs >= (UINTPTR)helm->GetTextContentLength())
  411. return OpStatus::OK;
  412. UINT32 ofs = (UINT32)_ofs;
  413. if(!helm->GetLayoutBox() || !helm->GetLayoutBox()->IsTextBox() || !helm->IsIncludedActual())
  414. return OpStatus::OK;
  415. if(m_spell_session->IsWordSeparator(helm->TextContent()[ofs]))
  416. return OpStatus::OK;
  417. m_replace_word.SetAtWord(helm, ofs);
  418. if(!*m_replace_word.GetCurrentWord())
  419. return OpStatus::OK;
  420. OpSpellCheckerSession temp_session(g_internal_spellcheck, m_spell_session->GetLanguage(), g_spell_ui_data, TRUE, m_spell_session->GetIgnoreWords());
  421. if(temp_session.CanSpellCheck(m_replace_word.GetCurrentWord()))
  422. {
  423. spell_session_id = g_spell_ui_data->CreateIdFor(this);
  424. RETURN_IF_ERROR(temp_session.CheckText(&m_replace_word, TRUE));
  425. }
  426. return OpStatus::OK;
  427. }
  428. OP_STATUS OpDocumentEdit::CreateSpellUISession(IntersectionObject *intersection, int &spell_session_id)
  429. {
  430. OP_STATUS status = CreateSpellUISessionInternal(intersection, spell_session_id);
  431. if(OpStatus::IsError(status))
  432. {
  433. spell_session_id = 0;
  434. g_spell_ui_data->InvalidateData();
  435. return status;
  436. }
  437. if(!spell_session_id && (g_internal_spellcheck->HasInstalledLanguages() || m_spell_session))
  438. spell_session_id = g_spell_ui_data->CreateIdFor(this);
  439. return OpStatus::OK;
  440. }
  441. OP_STATUS OpDocumentEdit::ReplaceWord(OpSpellCheckerWordIterator *word_iterator, const uni_char *new_word)
  442. {
  443. HTML_WordIterator *word = (HTML_WordIterator*)word_iterator;
  444. HTML_Element *start_helm = word->CurrentStartHelm();
  445. HTML_Element *stop_helm = word->CurrentStopHelm();
  446. int start_ofs = word->CurrentStartOfs();
  447. int stop_ofs = word->CurrentStopOfs();
  448. if(!GetValidCaretPosFrom(start_helm, start_ofs, start_helm, start_ofs))
  449. return OpStatus::OK;
  450. if(!GetValidCaretPosFrom(stop_helm, stop_ofs, stop_helm, stop_ofs))
  451. return OpStatus::OK;
  452. OldStyleTextSelectionPoint start;
  453. OldStyleTextSelectionPoint stop;
  454. start.SetLogicalPosition(start_helm, start_ofs);
  455. stop.SetLogicalPosition(stop_helm, stop_ofs);
  456. m_selection.Select(&start, &stop);
  457. RETURN_IF_ERROR(InsertText(new_word, uni_strlen(new_word)));
  458. return OpStatus::OK;
  459. }
  460. void OpDocumentEdit::OnBeforeNewCaretPos(HTML_Element *helm, int ofs)
  461. {
  462. if(!m_spell_session || !m_delay_misspell_word_info)
  463. return;
  464. Text_Box *box = GetTextBox(helm);
  465. if(m_doc_has_changed || !box)
  466. {
  467. m_delay_misspell_word_info = NULL;
  468. RepaintElement(m_caret.GetElement());
  469. return;
  470. }
  471. WordInfo *w = box->GetWords();
  472. WordInfo *end = w + box->GetWordCount();
  473. for(; w != end; w++)
  474. {
  475. if(static_cast<int>(w->GetStart()) <= ofs && static_cast<int>(w->GetStart()) + static_cast<int>(w->GetLength()) >= ofs)
  476. break;
  477. }
  478. if(w == m_delay_misspell_word_info)
  479. return;
  480. m_delay_misspell_word_info = NULL;
  481. RepaintElement(m_caret.GetElement());
  482. }
  483. void OpDocumentEdit::DoSpellWordInfoUpdate(SpellWordInfoObject *old_info)
  484. {
  485. if(!m_spell_session || !old_info->IsValid() || old_info->helm != m_caret.GetElement())
  486. return;
  487. Text_Box *box = GetTextBox(m_caret.GetElement());
  488. if(!box || box->GetWordCount() == old_info->word_count || m_caret.GetOffset() >= m_caret.GetElement()->GetTextContentLength())
  489. return;
  490. int i,j;
  491. WordInfo *wi = box->GetWords();
  492. int word_count = box->GetWordCount();
  493. int steps = word_count - old_info->word_count;
  494. if(!wi || !word_count)
  495. return;
  496. if(steps > 0)
  497. {
  498. for(i = word_count-1; i >= 0 ; i--)
  499. {
  500. if(i < steps || wi[i].GetStart() <= (UINT32)m_caret.GetOffset())
  501. {
  502. for(j = steps; i >= 0 && j > 0; j--, i--)
  503. wi[i].SetIsMisspelling(FALSE);
  504. break;
  505. }
  506. wi[i].SetIsMisspelling(wi[i-steps].IsMisspelling());
  507. }
  508. }
  509. else // steps < 0
  510. {
  511. for(i = 0; i < word_count && wi[i].GetStart() <= (UINT32)m_caret.GetOffset(); i++) {}
  512. for(;i - steps < word_count;i++)
  513. wi[i].SetIsMisspelling(wi[i-steps].IsMisspelling());
  514. wi[word_count-1].SetIsMisspelling(old_info->last_word_misspelled);
  515. }
  516. }
  517. void OpDocumentEdit::PossiblyDelayMisspell(BOOL was_delayed_misspell)
  518. {
  519. HTML_Element *helm = m_caret.GetElement();
  520. int ofs = m_caret.GetOffset();
  521. if(!m_spell_session || !helm || helm->Type() != HE_TEXT || !helm->GetLayoutBox() || !helm->GetLayoutBox()->IsTextBox())
  522. return;
  523. Text_Box *box = (Text_Box*)helm->GetLayoutBox();
  524. WordInfo *w = box->GetWords();
  525. WordInfo *end = w + box->GetWordCount();
  526. for(; w != end; w++)
  527. {
  528. if(static_cast<int>(w->GetStart() + w->GetLength()) == ofs)
  529. break;
  530. }
  531. if(w == end || !w->GetLength() || !was_delayed_misspell && w->GetLength() != 1)
  532. return;
  533. const uni_char *txt = helm->TextContent();
  534. int txt_len = helm->GetTextContentLength();
  535. unsigned int i;
  536. for(i=0;i<w->GetLength();i++)
  537. {
  538. if(m_spell_session->IsWordSeparator(txt[w->GetStart()+i]))
  539. return;
  540. }
  541. BOOL start_ok = FALSE;
  542. if(w->GetStart())
  543. {
  544. if(m_spell_session->IsWordSeparator(txt[w->GetStart()-1]))
  545. start_ok = TRUE;
  546. else
  547. return;
  548. }
  549. if(static_cast<int>(w->GetStart()+w->GetLength()) < txt_len)
  550. {
  551. if(m_spell_session->IsWordSeparator(txt[w->GetStart()+w->GetLength()]))
  552. {
  553. if(start_ok)
  554. {
  555. m_delay_misspell_word_info = w;
  556. return;
  557. }
  558. }
  559. else
  560. return;
  561. }
  562. HTML_Element *tmp;
  563. if(!start_ok)
  564. {
  565. tmp = helm->Prev();
  566. while(tmp && IsFriendlyElement(tmp))
  567. {
  568. if(tmp->Type() == HE_TEXT && tmp->GetTextContentLength() && tmp->TextContent())
  569. {
  570. if(m_spell_session->IsWordSeparator(tmp->TextContent()[tmp->GetTextContentLength()-1]))
  571. break;
  572. else
  573. return;
  574. }
  575. tmp = tmp->Prev();
  576. }
  577. }
  578. else // start_ok
  579. {
  580. HTML_Element *container = m_doc->GetCaret()->GetContainingElement(helm);
  581. HTML_Element *stop_elm = container ? (HTML_Element*)container->NextSibling() : NULL;
  582. tmp = helm->Next();
  583. while(tmp && tmp != stop_elm && IsFriendlyElement(tmp))
  584. {
  585. if(tmp->Type() == HE_TEXT && tmp->GetTextContentLength() && tmp->TextContent())
  586. {
  587. if(m_spell_session->IsWordSeparator(tmp->TextContent()[0]))
  588. break;
  589. else
  590. return;
  591. }
  592. tmp = tmp->Next();
  593. }
  594. }
  595. m_delay_misspell_word_info = w;
  596. }
  597. //===================================HTML_WordIterator===================================
  598. void HTML_WordIterator::Reset(BOOL stop_session)
  599. {
  600. m_active = FALSE;
  601. m_been_used = FALSE;
  602. m_single_word = FALSE;
  603. m_current_start = NULL;
  604. m_current_stop = NULL;
  605. m_editable_container = NULL;
  606. m_after_editable_container = NULL;
  607. m_current_start_ofs = 0;
  608. m_current_stop_ofs = 0;
  609. m_first = NULL;
  610. m_last = NULL;
  611. if(m_current_string.CStr())
  612. m_current_string.Set(UNI_L(""));
  613. if(stop_session && m_used_by_session)
  614. {
  615. OpSpellCheckerSession *session = m_edit->GetSpellCheckerSession();
  616. if(session)
  617. {
  618. m_edit->OnSpellcheckerStopped();
  619. session->StopCurrentSpellChecking();
  620. }
  621. }
  622. }
  623. HTML_WordIterator::HTML_WordIterator(OpDocumentEdit *edit, BOOL used_by_session, BOOL mark_separators) : OpDocumentEditInternalEventListener()
  624. {
  625. m_edit = edit;
  626. m_used_by_session = used_by_session;
  627. m_mark_separators = mark_separators;
  628. Reset();
  629. }
  630. HTML_WordIterator::~HTML_WordIterator()
  631. {
  632. }
  633. void HTML_WordIterator::SetRange(HTML_Element *first, HTML_Element *last)
  634. {
  635. Reset();
  636. m_editable_container = m_edit->GetEditableContainer(first);
  637. if(!m_editable_container)
  638. {
  639. if(!m_edit->GetDoc() || m_edit->GetDoc()->GetDesignMode())
  640. {
  641. OP_ASSERT(FALSE);
  642. return;
  643. }
  644. while(first && first != last && !first->IsContentEditable(FALSE))
  645. first = first->NextActual();
  646. if(!first || first == last)
  647. return;
  648. m_editable_container = first;
  649. }
  650. m_after_editable_container = m_editable_container->NextSiblingActual();
  651. m_edit->AddInternalEventListener(this);
  652. m_first = first;
  653. m_last = last;
  654. m_active = TRUE;
  655. m_current_start = m_first;
  656. m_current_stop = m_first;
  657. ContinueIteration();
  658. }
  659. void HTML_WordIterator::SetAtWord(HTML_Element *helm, int ofs)
  660. {
  661. Reset();
  662. HTML_Element *editable_container = m_edit->GetEditableContainer(helm);
  663. if(!editable_container)
  664. return;
  665. OpSpellCheckerSession *session = m_edit->GetSpellCheckerSession();
  666. if(session->IsWordSeparator(helm->TextContent()[ofs]))
  667. return;
  668. HTML_Element *tmp = helm;
  669. unsigned tmp_ofs = ofs;
  670. HTML_Element* stop = m_edit->GetDoc()->GetCaret()->GetContainingElement(helm);
  671. if (!stop || stop->IsAncestorOf(editable_container))
  672. stop = editable_container;
  673. while(tmp)
  674. {
  675. const uni_char* txt=tmp->Content();
  676. while(tmp_ofs > 0 && !session->IsWordSeparator(txt[tmp_ofs-1]))
  677. tmp_ofs--;
  678. if(tmp_ofs != tmp->ContentLength())
  679. {
  680. helm = tmp;
  681. ofs = tmp_ofs;
  682. }
  683. if(tmp_ofs || tmp==stop)
  684. break;
  685. do
  686. {
  687. tmp = tmp->PrevActual();
  688. if(!tmp || !m_edit->IsFriendlyElement(tmp))
  689. {
  690. tmp = NULL;
  691. break;
  692. }
  693. } while(!tmp->ContentLength() && tmp!=stop);
  694. if(tmp)
  695. tmp_ofs = tmp->ContentLength();
  696. }
  697. if (helm->SpellcheckEnabledByAttr() < 0)
  698. return;
  699. m_editable_container = editable_container;
  700. m_after_editable_container = m_editable_container->NextSiblingActual();
  701. m_edit->AddInternalEventListener(this);
  702. m_active = TRUE;
  703. m_current_start = helm;
  704. m_current_stop = helm;
  705. m_current_start_ofs = ofs;
  706. m_current_stop_ofs = ofs;
  707. m_first = helm;
  708. m_last = m_editable_container->LastLeafActual(); // or maybe it could be NULL...
  709. ContinueIteration();
  710. if(m_active)
  711. m_single_word = TRUE;
  712. }
  713. BOOL HTML_WordIterator::FindStartOfWord()
  714. {
  715. OpSpellCheckerSession *session = m_edit->m_spell_session;
  716. HTML_Element *helm = m_current_stop;
  717. int ofs = m_current_stop_ofs;
  718. m_current_start = m_current_stop;
  719. m_current_start_ofs = m_current_stop_ofs;
  720. OP_ASSERT(helm != m_after_editable_container);
  721. BOOL start_found = FALSE;
  722. while(helm)
  723. {
  724. //FIXME: Account for first letter pseudo element?
  725. const uni_char* txt = helm->Content();
  726. int txt_len = helm->ContentLength();
  727. OP_ASSERT(ofs <= txt_len);
  728. for(;ofs<txt_len && session->IsWordSeparator(txt[ofs]);ofs++) {}
  729. m_current_stop = helm;
  730. m_current_stop_ofs = ofs;
  731. if(ofs != txt_len)
  732. {
  733. start_found = TRUE;
  734. break;
  735. }
  736. if (helm == m_last)
  737. break;
  738. ofs = 0;
  739. helm = helm->NextActual();
  740. if(helm == m_after_editable_container)
  741. {
  742. if(!m_edit->GetDoc() || m_edit->GetDoc()->GetDesignMode())
  743. break;
  744. while(helm && helm != m_last && !helm->IsContentEditable(FALSE))
  745. helm = helm->NextActual();
  746. if(!helm || !helm->IsContentEditable(FALSE))
  747. break;
  748. m_editable_container = helm;
  749. m_after_editable_container = helm->NextSiblingActual();
  750. }
  751. }
  752. if(m_mark_separators)
  753. m_edit->MarkNodesMisspelled(FALSE, this);
  754. m_current_start = m_current_stop;
  755. m_current_start_ofs = m_current_stop_ofs;
  756. return start_found;
  757. }
  758. BOOL HTML_WordIterator::ReadWord(OpString* toString)
  759. {
  760. int i;
  761. OpSpellCheckerSession *session = m_edit->m_spell_session;
  762. HTML_Element *helm = m_current_stop;
  763. int ofs = m_current_stop_ofs;
  764. HTML_Element *stop = m_edit->GetDoc()->GetCaret()->GetContainingElement(helm);
  765. if(stop && !stop->IsAncestorOf(m_after_editable_container))
  766. stop = stop->NextSiblingActual();
  767. else
  768. stop = m_after_editable_container;
  769. while(helm)
  770. {
  771. const uni_char* txt = helm->Content();
  772. int txt_len = helm->ContentLength();
  773. for(i = ofs; i<txt_len && !session->IsWordSeparator(txt[i]); i++) {}
  774. if(toString && OpStatus::IsError(toString->Append(txt+ofs, i-ofs)))
  775. {
  776. m_current_stop = helm;
  777. m_current_stop_ofs = i;
  778. if(m_mark_separators)
  779. m_edit->MarkNodesMisspelled(FALSE, this);
  780. Reset(!m_been_used);
  781. return FALSE;
  782. }
  783. ofs = i;
  784. if(ofs != txt_len)
  785. break; // we found a word separator and are finished
  786. if(helm == m_last)
  787. break;
  788. HTML_Element *next = helm->NextActual();
  789. // Skip past elements with no text in them.
  790. while(next && next != stop)
  791. {
  792. if(next->ContentLength())
  793. break;
  794. if(next == m_last || !m_edit->IsFriendlyElement(next))
  795. {
  796. next = stop;
  797. break;
  798. }
  799. next = next->NextActual();
  800. }
  801. if(next == stop)
  802. break;
  803. helm = next;
  804. ofs = 0;
  805. }
  806. m_current_stop = helm;
  807. m_current_stop_ofs = ofs;
  808. if(!toString && m_mark_separators)
  809. m_edit->MarkNodesMisspelled(FALSE, this);
  810. return TRUE;
  811. }
  812. BOOL HTML_WordIterator::ContinueIteration()
  813. {
  814. if(!m_active || m_single_word)
  815. return FALSE;
  816. m_current_string.Empty();
  817. BOOL word_found = FALSE;
  818. while (!word_found)
  819. {
  820. if(!FindStartOfWord())
  821. break;
  822. word_found = m_current_stop->SpellcheckEnabledByAttr() > 0;
  823. if(!word_found)
  824. ReadWord(NULL);
  825. }
  826. if (!word_found)
  827. {
  828. Reset(!m_been_used);
  829. return FALSE;
  830. }
  831. if (!ReadWord(&m_current_string))
  832. return FALSE;
  833. OP_ASSERT(!m_current_string.IsEmpty());
  834. return TRUE;
  835. }
  836. void HTML_WordIterator::RestartFromBeginning()
  837. {
  838. RestartFrom(m_first);
  839. }
  840. void HTML_WordIterator::RestartFrom(HTML_Element *helm)
  841. {
  842. if(m_single_word)
  843. { // the single word we where supposed to hold got messed up...
  844. Reset();
  845. return;
  846. }
  847. //FIXME: What if helm is in the middle of a word.
  848. m_current_start = helm;
  849. m_current_stop = helm;
  850. m_current_start_ofs = 0;
  851. m_current_stop_ofs = 0;
  852. if(m_current_string.CStr())
  853. m_current_string.Set(UNI_L(""));
  854. }
  855. void HTML_WordIterator::OnElementOut(HTML_Element *helm)
  856. {
  857. if(!m_active)
  858. return;
  859. BOOL first_ancestor = helm->IsAncestorOf(m_first);
  860. BOOL last_ancestor = helm->IsAncestorOf(m_last);
  861. if(helm->IsAncestorOf(m_editable_container) || first_ancestor && last_ancestor)
  862. {
  863. Reset();
  864. return;
  865. }
  866. if(helm->IsAncestorOf(m_after_editable_container))
  867. m_after_editable_container = helm->NextSiblingActual();
  868. if(first_ancestor)
  869. m_first = helm->NextSiblingActual();
  870. if(last_ancestor)
  871. m_last = helm->PrevActual();
  872. if(!m_first || !m_last)
  873. {
  874. OP_ASSERT(!"should not be possible");
  875. Reset();
  876. return;
  877. }
  878. if(helm->IsAncestorOf(m_current_start))
  879. {
  880. if(first_ancestor)
  881. RestartFrom(m_first);
  882. else
  883. {
  884. helm = helm->PrevActual();
  885. if(!helm)
  886. {
  887. OP_ASSERT(!"should not be possible");
  888. Reset();
  889. return;
  890. }
  891. RestartFrom(helm);
  892. }
  893. return;
  894. }
  895. BOOL restart_from_start = helm->IsAncestorOf(m_current_stop);
  896. if(!restart_from_start)
  897. {
  898. HTML_Element *tmp = m_current_start;
  899. while(tmp && tmp != m_current_stop)
  900. {
  901. if(tmp == helm)
  902. {
  903. restart_from_start = TRUE;
  904. break;
  905. }
  906. tmp = tmp->NextActual();
  907. }
  908. if(!tmp)
  909. {
  910. OP_ASSERT(FALSE);
  911. Reset();
  912. return;
  913. }
  914. }
  915. if(restart_from_start)
  916. RestartFrom(m_current_start);
  917. }
  918. void HTML_WordIterator::OnElementInserted(HTML_Element *helm)
  919. {
  920. if(!m_active)
  921. return;
  922. if(m_last->IsAncestorOf(helm))
  923. {
  924. m_last = m_last->LastLeaf();
  925. while(m_last != m_last && (m_last->Type() != HE_TEXT || !m_last->IsIncludedActual()))
  926. m_last = m_last->Prev();
  927. }
  928. }
  929. void HTML_WordIterator::OnTextConvertedToTextGroup(HTML_Element* elm)
  930. {
  931. // Our pointers are to text element, not to textgroups. If a text element
  932. // changes we need to modify the pointers as well.
  933. if (m_current_start == elm)
  934. m_current_start = elm->FirstChild();
  935. if (m_current_stop == elm)
  936. m_current_stop = elm->FirstChild();
  937. if (m_first == elm)
  938. m_first = elm->FirstChild();
  939. if (m_last == elm)
  940. m_last = elm->FirstChild();
  941. }
  942. BOOL HTML_WordIterator::IsInRange(HTML_Element* elm)
  943. {
  944. if (!m_last || !m_first)
  945. return FALSE;
  946. HTML_Element* iter = m_first;
  947. while (iter && iter != m_last->NextActual())
  948. {
  949. if (iter == elm)
  950. return TRUE;
  951. iter = iter->NextActual();
  952. }
  953. return FALSE;
  954. }
  955. #endif // DOCUMENT_EDIT_SUPPORT && INTERNAL_SPELLCHECK_SUPPORT