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.

OpDocumentEditUtils.cpp 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. /* -*- Mode: c++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
  2. *
  3. * Copyright (C) 1995-2011 Opera Software ASA. 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. #ifdef DOCUMENT_EDIT_SUPPORT
  10. #include "modules/documentedit/OpDocumentEdit.h"
  11. #include "modules/documentedit/OpDocumentEditUtils.h"
  12. #ifdef _DOCEDIT_DEBUG
  13. #include "modules/documentedit/OpDocumentEditUndoRedo.h"
  14. void OpDocumentEditDebugChecker::UpdateCallerInfo(void *call_addr)
  15. {
  16. // To be implemented...
  17. }
  18. OpDocumentEditDebugChecker::OpDocumentEditDebugChecker(OpDocumentEdit *edit, OpDocumentEditDebugCheckerObject *obj, BOOL check_begin_count, BOOL static_context)
  19. {
  20. #define __CALL_ADDRESS__ ((void**)(&edit))[-1]
  21. m_static_context = static_context;
  22. if(!static_context)
  23. {
  24. m_edit = edit;
  25. m_obj = obj;
  26. if(!m_edit || !obj)
  27. {
  28. OP_ASSERT(FALSE);
  29. m_check_begin_count = 0;
  30. return;
  31. }
  32. m_check_begin_count = obj->CheckBeginCount() && check_begin_count;
  33. if(m_check_begin_count)
  34. m_begin_count = edit->m_undo_stack.GetBeginCount();
  35. }
  36. UpdateCallerInfo(__CALL_ADDRESS__);
  37. }
  38. OpDocumentEditDebugChecker::~OpDocumentEditDebugChecker()
  39. {
  40. if(!m_static_context)
  41. {
  42. if(m_check_begin_count)
  43. OP_ASSERT(m_edit->m_undo_stack.GetBeginCount() == m_begin_count);
  44. }
  45. }
  46. #endif // _DOCEDIT_DEBUG
  47. Text_Box *GetTextBox(HTML_Element *helm)
  48. {
  49. if(!helm || helm->Type() != HE_TEXT || !helm->GetLayoutBox() || !helm->GetLayoutBox()->IsTextBox())
  50. return NULL;
  51. return (Text_Box*)helm->GetLayoutBox();
  52. }
  53. /** Returns TRUE if helm is a non-collapsed empty text-element. */
  54. BOOL IsNonCollapsedEmptyTextElm(HTML_Element *helm)
  55. {
  56. DEBUG_CHECKER_STATIC();
  57. Text_Box *box;
  58. if(!helm || helm->Type() != HE_TEXT || helm->GetTextContentLength() || !helm->GetLayoutBox() || !helm->GetLayoutBox()->IsTextBox())
  59. return FALSE;
  60. box = (Text_Box*)helm->GetLayoutBox();
  61. return box->GetWordCount() == 1 && box->GetWords() && !box->GetWords()->IsCollapsed();
  62. }
  63. OpDocumentEditAutoLink *OpDocumentEditAutoLink::GetLink(void *object, Head *head)
  64. {
  65. if(!head)
  66. return NULL;
  67. OpDocumentEditAutoLink *link = (OpDocumentEditAutoLink*)head->First();
  68. while(link)
  69. {
  70. if(link->GetObject() == object)
  71. return link;
  72. link = (OpDocumentEditAutoLink*)link->Suc();
  73. }
  74. return NULL;
  75. }
  76. OpDocumentEditRangeKeeper::OpDocumentEditRangeKeeper(HTML_Element *container, HTML_Element *start_elm, HTML_Element *stop_elm, OpDocumentEdit *edit) : OpDocumentEditInternalEventListener()
  77. {
  78. DEBUG_CHECKER_CONSTRUCTOR();
  79. m_pending_start_elm = m_pending_stop_elm = NULL;
  80. if(!edit || !container || !start_elm || !stop_elm || !container->IsAncestorOf(start_elm) || !container->IsAncestorOf(stop_elm))
  81. {
  82. OP_ASSERT(FALSE);
  83. MakeInvalid();
  84. return;
  85. }
  86. m_edit = edit;
  87. m_container = container;
  88. m_start_elm = start_elm;
  89. m_stop_elm = stop_elm;
  90. m_edit->AddInternalEventListener(this);
  91. }
  92. HTML_Element *OpDocumentEditRangeKeeper::AdjustInDirection(HTML_Element *helm, BOOL forward, BOOL only_allow_in_direction)
  93. {
  94. DEBUG_CHECKER(TRUE);
  95. HTML_Element *org_helm = helm;
  96. if(forward)
  97. {
  98. HTML_Element *after_container = (HTML_Element*)m_container->NextSibling();
  99. helm = (HTML_Element*)helm->NextSibling();
  100. while(helm && helm != after_container && !helm->IsIncludedActual())
  101. helm = helm->Next();
  102. if(helm == after_container)
  103. helm = NULL;
  104. }
  105. else
  106. {
  107. helm = helm->Prev();
  108. while(helm && helm != m_container && !helm->IsIncludedActual())
  109. helm = helm->Prev();
  110. }
  111. return helm ? helm : (only_allow_in_direction ? NULL : AdjustInDirection(org_helm,!forward,TRUE));
  112. }
  113. void OpDocumentEditRangeKeeper::OnElementInserted(HTML_Element *helm)
  114. {
  115. DEBUG_CHECKER(TRUE);
  116. BOOL inserted_start_stop = FALSE;
  117. if(!helm || !IsValid())
  118. return;
  119. if(m_pending_start_elm && helm->IsAncestorOf(m_pending_start_elm) && m_container->IsAncestorOf(m_pending_start_elm))
  120. {
  121. inserted_start_stop = TRUE;
  122. m_start_elm = m_pending_start_elm;
  123. m_pending_start_elm = NULL;
  124. }
  125. if(m_pending_stop_elm && helm->IsAncestorOf(m_pending_stop_elm) && m_container->IsAncestorOf(m_pending_stop_elm))
  126. {
  127. inserted_start_stop = TRUE;
  128. m_stop_elm = m_pending_stop_elm;
  129. m_pending_stop_elm = NULL;
  130. }
  131. if(inserted_start_stop && m_stop_elm->Precedes(m_start_elm))
  132. {
  133. HTML_Element *tmp = m_start_elm;
  134. m_start_elm = m_stop_elm;
  135. m_stop_elm = tmp;
  136. }
  137. }
  138. void OpDocumentEditRangeKeeper::OnElementOut(HTML_Element *helm)
  139. {
  140. DEBUG_CHECKER(TRUE);
  141. if(!helm || !IsValid())
  142. return;
  143. BOOL is_start_ancestor = helm->IsAncestorOf(m_start_elm);
  144. BOOL is_stop_ancestor = helm->IsAncestorOf(m_stop_elm);
  145. BOOL is_container_ancestor = helm->IsAncestorOf(m_container);
  146. if((!is_start_ancestor && !is_stop_ancestor) || is_container_ancestor)
  147. return;
  148. if(is_start_ancestor)
  149. {
  150. if(!m_pending_start_elm)
  151. m_pending_start_elm = m_start_elm;
  152. m_start_elm = AdjustInDirection(helm,FALSE);
  153. }
  154. if(is_stop_ancestor)
  155. {
  156. if(!m_pending_stop_elm)
  157. m_pending_stop_elm = m_stop_elm;
  158. m_stop_elm = AdjustInDirection(helm,TRUE);
  159. }
  160. }
  161. void OpDocumentEditRangeKeeper::OnElementDeleted(HTML_Element *helm)
  162. {
  163. DEBUG_CHECKER(TRUE);
  164. if(!helm || !IsValid())
  165. return;
  166. if(helm->IsAncestorOf(m_container))
  167. {
  168. MakeInvalid();
  169. return;
  170. }
  171. if(helm->IsAncestorOf(m_start_elm))
  172. {
  173. OP_ASSERT(FALSE); // We seems to have missed OnElementOut!
  174. m_start_elm = m_container;
  175. }
  176. if(helm->IsAncestorOf(m_stop_elm))
  177. {
  178. OP_ASSERT(FALSE); // We seems to have missed OnElementOut!
  179. m_stop_elm = m_container;
  180. }
  181. if(m_pending_start_elm && helm->IsAncestorOf(m_pending_start_elm))
  182. m_pending_start_elm = NULL;
  183. if(m_pending_stop_elm && helm->IsAncestorOf(m_pending_stop_elm))
  184. m_pending_stop_elm = NULL;
  185. }
  186. OpDocumentEditWsPreserverContainer::OpDocumentEditWsPreserverContainer(OpDocumentEditWsPreserver *preserver, OpDocumentEdit *edit)
  187. {
  188. m_preserver = preserver;
  189. m_edit = edit;
  190. m_edit->AddInternalEventListener(this);
  191. }
  192. void OpDocumentEditWsPreserverContainer::OnElementDeleted(HTML_Element *helm)
  193. {
  194. if (helm == m_preserver->GetStartElement())
  195. m_preserver->SetStartElement(NULL);
  196. if (helm == m_preserver->GetStopElement())
  197. m_preserver->SetStopElement(NULL);
  198. }
  199. OpDocumentEditWsPreserver::OpDocumentEditWsPreserver(HTML_Element *start_elm, HTML_Element *stop_elm, int start_ofs, int stop_ofs, OpDocumentEdit *edit)
  200. {
  201. DEBUG_CHECKER_CONSTRUCTOR();
  202. m_edit = edit;
  203. if(edit)
  204. SetRemoveRange(start_elm,stop_elm,start_ofs,stop_ofs);
  205. else
  206. ClearRange();
  207. }
  208. BOOL OpDocumentEditWsPreserver::SetRemoveRange(HTML_Element *start_elm, HTML_Element *stop_elm, int start_ofs, int stop_ofs, BOOL check_start)
  209. {
  210. DEBUG_CHECKER(TRUE);
  211. if(!m_edit)
  212. return FALSE;
  213. if(!check_start)
  214. start_elm = NULL;
  215. start_ofs--;
  216. if(start_elm && (start_ofs < 0 || start_elm->Type() != HE_TEXT))
  217. {
  218. for(start_elm = start_elm->PrevActual(); start_elm; start_elm = start_elm->PrevActual())
  219. {
  220. if(start_elm->Type() == HE_TEXT && start_elm->GetTextContentLength())
  221. {
  222. start_ofs = start_elm->GetTextContentLength()-1;
  223. break;
  224. }
  225. }
  226. }
  227. if(stop_elm && (stop_ofs >= stop_elm->GetTextContentLength() || stop_elm->Type() != HE_TEXT))
  228. {
  229. stop_ofs = 0;
  230. for(stop_elm = stop_elm->NextActual(); stop_elm; stop_elm = stop_elm->NextActual())
  231. if(stop_elm->Type() == HE_TEXT && stop_elm->GetTextContentLength())
  232. break;
  233. }
  234. if(start_elm && (!start_elm->TextContent() || !uni_collapsing_sp(start_elm->TextContent()[start_ofs])))
  235. start_elm = NULL;
  236. if(stop_elm && (!stop_elm->TextContent() || !uni_collapsing_sp(stop_elm->TextContent()[stop_ofs])))
  237. stop_elm = NULL;
  238. BOOL check_stop_elm = stop_elm && start_elm != stop_elm;
  239. OP_STATUS status = OpStatus::OK;
  240. if(start_elm)
  241. {
  242. OpDocumentEditWordIterator iter(start_elm,m_edit);
  243. status = iter.GetStatus();
  244. m_start_was_collapsed = iter.IsCharCollapsed(start_ofs);
  245. if(start_elm == stop_elm)
  246. m_stop_was_collapsed = iter.IsCharCollapsed(stop_ofs);
  247. }
  248. if(OpStatus::IsSuccess(status) && check_stop_elm)
  249. {
  250. OpDocumentEditWordIterator iter(stop_elm,m_edit);
  251. status = iter.GetStatus();
  252. m_stop_was_collapsed = iter.IsCharCollapsed(stop_ofs);
  253. }
  254. m_start_elm = start_elm;
  255. m_stop_elm = stop_elm;
  256. m_start_ofs = start_ofs;
  257. if(stop_elm)
  258. m_stop_ofs = stop_elm->GetTextContentLength() - stop_ofs;
  259. if(OpStatus::IsError(status))
  260. m_start_elm = m_stop_elm = NULL;
  261. BOOL preserving = m_start_elm || m_stop_elm;
  262. if(preserving)
  263. m_edit->AddWsPreservingOperation(this);
  264. return preserving;
  265. }
  266. BOOL OpDocumentEditWsPreserver::GetCollapsedCountFrom(HTML_Element *helm, int ofs, int &found_count)
  267. {
  268. DEBUG_CHECKER(TRUE);
  269. int i;
  270. found_count = 0;
  271. if(!helm || helm->Type() != HE_TEXT)
  272. return FALSE;
  273. int txt_len = helm->GetTextContentLength();
  274. if(!txt_len)
  275. return TRUE;
  276. OpDocumentEditWordIterator iter(helm,m_edit);
  277. if(OpStatus::IsError(iter.GetStatus()))
  278. return FALSE;
  279. for(i = ofs; i < txt_len && iter.IsCharCollapsed(i); i++, found_count++) {}
  280. return i >= txt_len;
  281. }
  282. HTML_Element *OpDocumentEditWsPreserver::GetNextFriendlyTextHelm(HTML_Element *helm)
  283. {
  284. DEBUG_CHECKER(TRUE);
  285. HTML_Element *after = helm->NextActual();
  286. while(after)
  287. {
  288. if(after->Type() == HE_TEXT)
  289. return after;
  290. if(!m_edit->IsFriends(helm,after))
  291. return NULL;
  292. after = after->NextActual();
  293. }
  294. return NULL;
  295. }
  296. BOOL OpDocumentEditWsPreserver::DeleteCollapsedFrom(HTML_Element *helm, int ofs, BOOL insert_nbsp_first)
  297. {
  298. DEBUG_CHECKER(TRUE);
  299. if(!helm || !m_edit)
  300. return FALSE;
  301. OpString str;
  302. HTML_Element *curr_helm = helm;
  303. int curr_ofs = ofs + 1;
  304. int count = 0, current_empty = 0;
  305. while(GetCollapsedCountFrom(curr_helm,curr_ofs,current_empty))
  306. {
  307. curr_helm = GetNextFriendlyTextHelm(curr_helm);
  308. count += current_empty;
  309. curr_ofs = 0;
  310. }
  311. count += current_empty;
  312. curr_helm = helm;
  313. curr_ofs = ofs;
  314. BOOL dont_set = FALSE;
  315. if(insert_nbsp_first)
  316. {
  317. if(OpStatus::IsError(str.Set(curr_helm->TextContent())))
  318. {
  319. m_edit->ReportOOM();
  320. return FALSE;
  321. }
  322. str.CStr()[curr_ofs++] = 0xA0;
  323. dont_set = TRUE;
  324. }
  325. else
  326. count++; // delete ofs too...
  327. BOOL has_changed = FALSE;
  328. for(;;)
  329. {
  330. if(!curr_helm)
  331. {
  332. OP_ASSERT(FALSE);
  333. return has_changed;
  334. }
  335. int to_delete = MIN(curr_helm->GetTextContentLength() - curr_ofs, count);
  336. if(curr_helm->GetTextContentLength())
  337. {
  338. if(!dont_set)
  339. {
  340. if(OpStatus::IsError(str.Set(curr_helm->TextContent())))
  341. {
  342. m_edit->ReportOOM();
  343. return has_changed;
  344. }
  345. }
  346. str.Delete(curr_ofs,to_delete);
  347. m_edit->SetElementText(curr_helm,str.CStr());
  348. has_changed = TRUE;
  349. }
  350. dont_set = FALSE;
  351. curr_ofs = 0;
  352. count -= to_delete;
  353. if(count)
  354. curr_helm = GetNextFriendlyTextHelm(curr_helm);
  355. else
  356. break;
  357. }
  358. return has_changed;
  359. }
  360. BOOL OpDocumentEditWsPreserver::WsPreserve()
  361. {
  362. DEBUG_CHECKER(TRUE);
  363. if(!m_edit)
  364. return FALSE;
  365. m_edit->RemoveWsPreservingOperation(this);
  366. BOOL preserved = FALSE;
  367. BOOL check_start = m_start_elm && m_start_elm->Type() == HE_TEXT && m_start_elm->GetTextContentLength() > m_start_ofs;
  368. BOOL check_stop = m_stop_elm && m_stop_elm->Type() == HE_TEXT && m_stop_elm->GetTextContentLength() >= m_stop_ofs;
  369. if(check_start)
  370. {
  371. OpDocumentEditWordIterator iter(m_start_elm,m_edit);
  372. if(OpStatus::IsError(iter.GetStatus()))
  373. return FALSE;
  374. if(iter.IsCharCollapsed(m_start_ofs) != m_start_was_collapsed)
  375. {
  376. OpString str;
  377. if (OpStatus::IsError(str.Set(m_start_elm->TextContent())))
  378. {
  379. m_edit->ReportOOM();
  380. return FALSE;
  381. }
  382. OP_ASSERT(m_start_elm->GetTextContentLength() == str.Length());
  383. // FIXME: We should also check the last previously collapsed char behind start to see if it has
  384. // became uncollapsed, then m_start_elm will still be collapsed however.
  385. if(m_start_was_collapsed)
  386. str.Delete(m_start_ofs,1);
  387. else
  388. str.CStr()[m_start_ofs] = 0xA0;
  389. m_edit->SetElementText(m_start_elm,str.CStr());
  390. preserved = TRUE;
  391. }
  392. }
  393. if(check_stop)
  394. {
  395. OpDocumentEditWordIterator iter(m_stop_elm,m_edit);
  396. if(OpStatus::IsError(iter.GetStatus()))
  397. return preserved;
  398. int ofs = m_stop_elm->GetTextContentLength() - m_stop_ofs;
  399. if(iter.IsCharCollapsed(ofs) != m_stop_was_collapsed)
  400. {
  401. preserved = DeleteCollapsedFrom(m_stop_elm,ofs,!m_stop_was_collapsed);
  402. }
  403. }
  404. return preserved;
  405. }
  406. OpDocumentEditWordIterator::~OpDocumentEditWordIterator()
  407. {
  408. DEBUG_CHECKER(TRUE);
  409. }
  410. OpDocumentEditWordIterator::OpDocumentEditWordIterator(HTML_Element* helm, OpDocumentEdit *edit) :
  411. WordInfoIterator(edit ? edit->GetDoc() : NULL, helm, &m_surround_checker),
  412. m_surround_checker(edit),
  413. m_status(OpStatus::OK),
  414. m_edit(edit),
  415. m_is_valid_for_caret(MAYBE)
  416. {
  417. DEBUG_CHECKER_CONSTRUCTOR();
  418. if(edit->GetRoot()->IsDirty())
  419. edit->ReflowAndUpdate();
  420. m_status = WordInfoIterator::Init();
  421. if (OpStatus::IsError(m_status))
  422. return;
  423. }
  424. /* virtual */
  425. BOOL DocEditWordIteratorSurroundChecker::HasWsPreservingElmBeside(HTML_Element* helm, BOOL before)
  426. {
  427. int i;
  428. HTML_Element *next = helm;
  429. if(!next)
  430. {
  431. OP_ASSERT(FALSE);
  432. return FALSE;
  433. }
  434. for(;;)
  435. {
  436. next = before ? next->Prev() : next->Next();
  437. if (!next)
  438. return FALSE;
  439. if(before && !m_edit->IsFriends(next, helm, TRUE, TRUE, FALSE))
  440. return FALSE;
  441. if(!before && !m_edit->IsFriends(helm, next, TRUE, TRUE, TRUE))
  442. return FALSE;
  443. if(!next->GetLayoutBox())
  444. continue; // or return FALSE???
  445. if(m_edit->IsReplacedElement(next) /*|| next->Type() == HE_BR*/)
  446. return TRUE;
  447. if(next->Type() != HE_TEXT || !next->GetLayoutBox()->IsTextBox())
  448. continue;
  449. Text_Box *next_box = (Text_Box*)next->GetLayoutBox();
  450. WordInfo *words = next_box->GetWords();
  451. int count = next_box->GetWordCount();
  452. if(!words || !count)
  453. continue;
  454. if(before && words[count-1].HasTrailingWhitespace())
  455. return FALSE;
  456. for (i = 0; i < count; i++)
  457. if (words[i].GetLength() && !words[i].IsCollapsed())
  458. return TRUE;
  459. }
  460. }
  461. BOOL OpDocumentEditWordIterator::IsValidForCaret(BOOL valid_if_possible)
  462. {
  463. DEBUG_CHECKER(TRUE);
  464. if(m_is_valid_for_caret != MAYBE)
  465. return m_is_valid_for_caret == YES;
  466. /*
  467. samuelp rocked :) if i understand this code correctly the
  468. intention here is to assign correct state to
  469. m_is_valid_for_caret and return the corresponding result using
  470. only one statement.
  471. */
  472. if(HasUnCollapsedChar())
  473. return (m_is_valid_for_caret = YES) == YES;
  474. if(!HasUnCollapsedWord())
  475. return (m_is_valid_for_caret = NO) != NO;
  476. if(valid_if_possible)
  477. return (m_is_valid_for_caret = YES) == YES;
  478. HTML_Element *current_caret = m_edit->m_caret.GetElement();
  479. if(current_caret && current_caret->Type() == HE_TEXT && !current_caret->GetTextContentLength())
  480. { // don't set m_is_valid_for_caret yet...
  481. if(current_caret == GetElement())
  482. return TRUE;
  483. if(!m_edit->IsBeforeOutElm(current_caret) && m_edit->IsFriends(current_caret,GetElement(),TRUE,FALSE,FALSE))
  484. return FALSE;
  485. }
  486. HTML_Element *tmp;
  487. for(tmp = GetElement()->NextActual(); tmp; tmp = tmp->NextActual())
  488. {
  489. if(!m_edit->IsBeforeOutElm(tmp))
  490. {
  491. if(tmp->Type() == HE_BR || !m_edit->IsFriends(tmp,GetElement(),TRUE,FALSE,FALSE))
  492. break;
  493. if(tmp->Type() == HE_TEXT)
  494. {
  495. if(IsNonCollapsedEmptyTextElm(tmp))
  496. {
  497. if(GetFullLength())
  498. return (m_is_valid_for_caret = NO) != NO;
  499. }
  500. else
  501. {
  502. OpDocumentEditWordIterator iter(tmp,m_edit);
  503. if(iter.HasUnCollapsedChar())
  504. return (m_is_valid_for_caret = NO) != NO;
  505. }
  506. }
  507. }
  508. }
  509. for(tmp = GetElement()->PrevActual(); tmp; tmp = tmp->PrevActual())
  510. {
  511. if(!m_edit->IsBeforeOutElm(tmp))
  512. {
  513. if(tmp->Type() == HE_BR || !m_edit->IsFriends(GetElement(),tmp,TRUE,FALSE,FALSE))
  514. break;
  515. if(tmp->Type() == HE_TEXT)
  516. {
  517. if(IsNonCollapsedEmptyTextElm(tmp))
  518. return (m_is_valid_for_caret = NO) != NO;
  519. else
  520. {
  521. OpDocumentEditWordIterator iter(tmp,m_edit);
  522. if(iter.HasUnCollapsedWord() && !(!iter.HasUnCollapsedChar() && !GetFullLength() && iter.GetFullLength()))
  523. return (m_is_valid_for_caret = NO) != NO;
  524. }
  525. }
  526. }
  527. }
  528. return (m_is_valid_for_caret = YES) == YES;
  529. }
  530. BOOL OpDocumentEditWordIterator::GetFirstValidCaretOfs(int &res_ofs)
  531. {
  532. DEBUG_CHECKER(TRUE);
  533. res_ofs = 0;
  534. if(!IsValidForCaret())
  535. return FALSE;
  536. if(HasUnCollapsedChar())
  537. res_ofs = FirstUnCollapsedOfs();
  538. return TRUE;
  539. }
  540. BOOL OpDocumentEditWordIterator::GetLastValidCaretOfs(int &res_ofs)
  541. {
  542. DEBUG_CHECKER(TRUE);
  543. res_ofs = 0;
  544. if(!IsValidForCaret())
  545. return FALSE;
  546. if(HasUnCollapsedChar())
  547. res_ofs = LastUnCollapsedOfs() + 1;
  548. return TRUE;
  549. }
  550. BOOL OpDocumentEditWordIterator::GetValidCaretOfsFrom(int ofs, int &res_ofs, BOOL forward)
  551. {
  552. DEBUG_CHECKER(TRUE);
  553. if(!IsValidForCaret())
  554. return FALSE;
  555. if(!HasUnCollapsedChar())
  556. {
  557. if((forward && ofs >= 0) || (!forward && ofs <= 0))
  558. return FALSE;
  559. res_ofs = 0;
  560. return TRUE;
  561. }
  562. #ifdef DEBUG_ENABLE_OPASSERT
  563. BOOL success = TRUE;
  564. #endif // DEBUG_ENABLE_OPASSERT
  565. if(forward)
  566. {
  567. if(ofs > LastUnCollapsedOfs())
  568. return FALSE;
  569. #ifdef DEBUG_ENABLE_OPASSERT
  570. success =
  571. #endif // DEBUG_ENABLE_OPASSERT
  572. GetOfsSnappedToUnCollapsed(ofs,res_ofs,TRUE);
  573. OP_ASSERT(success);
  574. if(res_ofs == LastUnCollapsedOfs())
  575. res_ofs++;
  576. else
  577. #ifdef DEBUG_ENABLE_OPASSERT
  578. success =
  579. #endif // DEBUG_ENABLE_OPASSERT
  580. GetNextUnCollapsedOfs(res_ofs,res_ofs);
  581. }
  582. else
  583. {
  584. if(ofs <= FirstUnCollapsedOfs())
  585. return FALSE;
  586. if(ofs > LastUnCollapsedOfs())
  587. {
  588. res_ofs = LastUnCollapsedOfs();
  589. return TRUE;
  590. }
  591. if(IsCharCollapsed(ofs) && !IsPreFormatted())
  592. #ifdef DEBUG_ENABLE_OPASSERT
  593. success =
  594. #endif // DEBUG_ENABLE_OPASSERT
  595. GetOfsSnappedToUnCollapsed(ofs,res_ofs,FALSE);
  596. else
  597. #ifdef DEBUG_ENABLE_OPASSERT
  598. success =
  599. #endif // DEBUG_ENABLE_OPASSERT
  600. GetPrevUnCollapsedOfs(ofs,res_ofs);
  601. }
  602. OP_ASSERT(success);
  603. return TRUE;
  604. }
  605. BOOL OpDocumentEditWordIterator::SnapToValidCaretOfs(int ofs, int &res_ofs)
  606. {
  607. DEBUG_CHECKER(TRUE);
  608. res_ofs = 0;
  609. if(!IsValidForCaret())
  610. return FALSE;
  611. if(!HasUnCollapsedChar())
  612. return TRUE;
  613. if(ofs > LastUnCollapsedOfs())
  614. res_ofs = LastUnCollapsedOfs() + 1;
  615. else
  616. GetOfsSnappedToUnCollapsed(ofs,res_ofs,TRUE);
  617. return TRUE;
  618. }
  619. BOOL OpDocumentEditWordIterator::GetCaretOfsWithoutCollapsed(int ofs, int &res_ofs)
  620. {
  621. DEBUG_CHECKER(TRUE);
  622. res_ofs = 0;
  623. if(!IsValidForCaret())
  624. return FALSE;
  625. if(!HasUnCollapsedChar())
  626. return TRUE;
  627. if(ofs >= LastUnCollapsedOfs())
  628. res_ofs = UnCollapsedCount();
  629. else
  630. {
  631. SnapToValidCaretOfs(ofs,ofs);
  632. CollapsedToUnCollapsedOfs(ofs,res_ofs);
  633. }
  634. return TRUE;
  635. }
  636. OP_STATUS NonActualElementPosition::Construct(HTML_Element* element, BOOL search_reference_forward /* FALSE */)
  637. {
  638. OP_ASSERT(m_actual_reference == NULL);
  639. OP_ASSERT(m_offset == 0);
  640. if (!element)
  641. return OpStatus::OK;
  642. while (element && !element->IsIncludedActual())
  643. {
  644. element = search_reference_forward ? element->Next() : element->Prev();
  645. ++m_offset;
  646. }
  647. if (!element)
  648. {
  649. m_offset = 0;
  650. return OpStatus::ERR;
  651. }
  652. m_actual_reference = element;
  653. return OpStatus::OK;
  654. }
  655. HTML_Element* NonActualElementPosition::Get() const
  656. {
  657. HTML_Element* result = m_actual_reference;
  658. for (unsigned int i = 0; result && i < m_offset; ++i)
  659. result = m_forward ? result->Prev() : result->Next();
  660. return result;
  661. }
  662. #endif // DOCUMENT_EDIT_SUPPORT