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.

OpDocumentEditCaret.cpp 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851
  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. #include "modules/documentedit/OpDocumentEdit.h"
  10. #include "modules/documentedit/OpDocumentEditUtils.h"
  11. #include "modules/layout/box/blockbox.h"
  12. #include "modules/layout/layout_workplace.h"
  13. #ifdef DOCUMENT_EDIT_SUPPORT
  14. OpDocumentEditCaret::OpDocumentEditCaret(OpDocumentEdit *edit)
  15. : m_edit(edit)
  16. // m_transform_root has suitable default constructor
  17. , m_parent_candidate(NULL)
  18. , m_remove_when_move_elm(NULL)
  19. , m_remove_when_move_id(0)
  20. , m_real_caret_elm_off(0)
  21. {
  22. }
  23. CaretManager* OpDocumentEditCaret::GetCaretManager()
  24. {
  25. return m_edit->m_doc->GetCaret();
  26. }
  27. HTML_Element* OpDocumentEditCaret::GetElement()
  28. {
  29. HTML_Element* elm = GetCaretManager()->GetElement();
  30. #ifdef DRAG_SUPPORT
  31. CaretPainter* painter = m_edit->GetDoc()->GetCaretPainter();
  32. if (painter->IsDragCaret())
  33. {
  34. int offset;
  35. TextSelection::ConvertPointToOldStyle(painter->GetDragCaretPoint(), elm, offset);
  36. }
  37. #endif // DRAG_SUPPORT
  38. return IsElementEditable(elm) ? elm : NULL;
  39. }
  40. int OpDocumentEditCaret::GetOffset()
  41. {
  42. int offset = GetCaretManager()->GetOffset();
  43. #ifdef DRAG_SUPPORT
  44. CaretPainter* painter = m_edit->GetDoc()->GetCaretPainter();
  45. if (painter->IsDragCaret())
  46. {
  47. HTML_Element* elm;
  48. TextSelection::ConvertPointToOldStyle(painter->GetDragCaretPoint(), elm, offset);
  49. }
  50. #endif // DRAG_SUPPORT
  51. return offset;
  52. }
  53. HTML_Element *GetAncestorOfType(HTML_Element *helm, HTML_ElementType type);
  54. void OpDocumentEditCaret::PlaceFirst(HTML_Element* edit_root, BOOL create_line_if_empty/*=TRUE*/)
  55. {
  56. DEBUG_CHECKER(TRUE);
  57. if (!edit_root && m_parent_candidate) // Use m_parent_candidate if there is one (contentEditable is active on some element)
  58. edit_root = m_parent_candidate;
  59. else if (!edit_root && IsElementEditable(m_edit->GetBody())) // No root specified. Use body if it's editable (designMode is active).
  60. edit_root = m_edit->GetBody();
  61. if (edit_root)
  62. {
  63. if (edit_root->IsDirty() || !edit_root->GetLayoutBox())
  64. return; // Can't place carets without a complete layout structure
  65. HTML_Element* editable_elm = m_edit->FindEditableElement(edit_root, TRUE, FALSE, TRUE);
  66. if (editable_elm && edit_root->IsAncestorOf(editable_elm))
  67. {
  68. Set(editable_elm, 0);
  69. }
  70. else if (create_line_if_empty)
  71. {
  72. BOOL has_body_ancestor = !!GetAncestorOfType(edit_root, HE_BODY);
  73. // Locking to avoid recursive calls to this method when setting cursor
  74. LockUpdatePos(TRUE, FALSE);
  75. HTML_Element *first_text_elm = CreateTemporaryCaretHelm(edit_root);
  76. LockUpdatePos(FALSE, FALSE);
  77. if(!first_text_elm)
  78. return; // TODO: fixmed OOM
  79. Set(first_text_elm,0);
  80. if(!has_body_ancestor)
  81. SetRemoveWhenMoveIfUntouched();
  82. }
  83. }
  84. }
  85. OP_STATUS OpDocumentEditCaret::Init(BOOL create_line_if_empty, HTML_Element* edit_root, BOOL only_create_line_if_body)
  86. {
  87. DEBUG_CHECKER(TRUE);
  88. if (!edit_root && m_parent_candidate) // Use m_parent_candidate if there is one (contentEditable is active on some element)
  89. edit_root = m_parent_candidate;
  90. else if (!edit_root && IsElementEditable(m_edit->GetBody())) // No root specified. Use body if it's editable (designMode is active).
  91. edit_root = m_edit->GetBody();
  92. if (!edit_root)
  93. return OpStatus::OK; // We can't initialize now. Wait for later.
  94. OP_ASSERT(IsElementEditable(edit_root));
  95. // Can't place carets in invisible elements since we need layout info to do it
  96. if (edit_root->IsDirty() || !edit_root->GetLayoutBox())
  97. return OpStatus::OK;
  98. PlaceFirst(edit_root, create_line_if_empty);
  99. OP_ASSERT(!edit_root->IsMatchingType(HE_HTML, NS_HTML));
  100. if (create_line_if_empty && !GetElement())
  101. {
  102. BOOL has_body_ancestor = !!GetAncestorOfType(edit_root, HE_BODY);
  103. if(has_body_ancestor || !only_create_line_if_body)
  104. {
  105. HTML_Element *first_text_elm = CreateTemporaryCaretHelm(edit_root);
  106. if(!first_text_elm)
  107. return OpStatus::ERR_NO_MEMORY;
  108. Set(first_text_elm,0);
  109. if(!has_body_ancestor)
  110. SetRemoveWhenMoveIfUntouched();
  111. }
  112. }
  113. if (IsValid())
  114. m_edit->GetDoc()->GetCaretPainter()->Invalidate();
  115. m_edit->GetDoc()->GetCaretPainter()->Reset();
  116. m_edit->GetDoc()->GetCaretPainter()->UpdatePos();
  117. UpdateWantedX();
  118. return OpStatus::OK;
  119. }
  120. BOOL OpDocumentEditCaret::IsValid()
  121. {
  122. DEBUG_CHECKER(TRUE);
  123. return m_edit->GetDoc()->GetCaretPainter()->GetHeight() != 2;
  124. }
  125. #ifdef DOCUMENTEDIT_SPATIAL_FRAMES_FOCUS
  126. FramesDocElm *FindFrame(int x, int y, FramesDocument* doc, FramesDocElm* frm)
  127. {
  128. if (frm && (x < frm->GetAbsX() ||
  129. y < frm->GetAbsY() ||
  130. x > frm->GetAbsX() + frm->GetWidth() ||
  131. y > frm->GetAbsY() + frm->GetHeight()))
  132. return NULL;
  133. if (doc)
  134. {
  135. FramesDocElm *child = doc->GetFrmDocRoot();
  136. while(child)
  137. {
  138. FramesDocElm *hit = FindFrame(x, y, (FramesDocument*)child->GetCurrentDoc(), child);
  139. if (hit)
  140. return hit;
  141. child = child->Suc();
  142. }
  143. child = doc->GetIFrmRoot();
  144. while(child)
  145. {
  146. FramesDocElm *hit = FindFrame(x, y, (FramesDocument*)child->GetCurrentDoc(), child);
  147. if (hit)
  148. return hit;
  149. child = child->Suc();
  150. }
  151. }
  152. if (frm)
  153. {
  154. FramesDocElm *child = frm->FirstChild();
  155. while(child)
  156. {
  157. FramesDocElm *hit = FindFrame(x, y, (FramesDocument*)child->GetCurrentDoc(), child);
  158. if (hit)
  159. return hit;
  160. child = child->Suc();
  161. }
  162. }
  163. return frm;
  164. }
  165. #endif
  166. void OpDocumentEditCaret::MoveSpatial(BOOL forward)
  167. {
  168. DEBUG_CHECKER(TRUE);
  169. HTML_Element* old_helm = GetElement();
  170. int old_ofs = GetOffset();
  171. int old_x = m_edit->GetDoc()->GetCaretPainter()->GetX();
  172. int old_y = m_edit->GetDoc()->GetCaretPainter()->GetY();
  173. int old_line_y = m_edit->GetDoc()->GetCaretPainter()->GetLineY();
  174. int old_line_height = m_edit->GetDoc()->GetCaretPainter()->GetLineHeight();
  175. Move(forward, FALSE);
  176. HTML_Element* moved_helm = GetElement();
  177. int moved_ofs = GetOffset();
  178. if (m_edit->GetDoc()->GetCaretPainter()->GetLineY() != old_line_y)
  179. {
  180. Set(old_helm, old_ofs);
  181. AffinePos old_ctm, ctm;
  182. RECT old_rect, rect;
  183. FramesDocument* doc = m_edit->GetDoc();
  184. m_edit->GetContentRectOfElement(m_edit->GetDoc()->GetCaret()->GetContainingElement(old_helm), old_ctm, old_rect);
  185. const int max_distance = 100;
  186. int delta = forward ? 3 : -3;
  187. int distance = delta;
  188. while (op_abs(distance) < max_distance)
  189. {
  190. if (GetCaretManager()->Place(old_ctm, old_x + distance, old_y + 1, old_x + distance, old_y + old_line_height - 1, TRUE, TRUE))
  191. if (m_edit->GetDoc()->GetCaretPainter()->GetX() != old_x)
  192. if (m_edit->GetDoc()->GetCaret()->GetContainingElement(GetElement()) != m_edit->GetDoc()->GetCaret()->GetContainingElement(old_helm))
  193. {
  194. m_edit->GetContentRectOfElement(m_edit->GetDoc()->GetCaret()->GetContainingElement(GetElement()), ctm, rect);
  195. if (forward && rect.left > old_rect.right || ctm != old_ctm)
  196. break;
  197. if (!forward && rect.right < old_rect.left || ctm != old_ctm)
  198. break;
  199. }
  200. distance += delta;
  201. }
  202. if (op_abs(distance) >= max_distance && doc->GetParentDoc())
  203. {
  204. #ifdef DOCUMENTEDIT_SPATIAL_FRAMES_FOCUS
  205. // Search for another frame
  206. int width = doc->GetDocManager()->GetFrame()->GetWidth();
  207. if ((forward && old_x + distance > width) ||
  208. (!forward && old_x + distance < 0))
  209. {
  210. FramesDocElm* frm = doc->GetDocManager()->GetFrame();
  211. if (frm)
  212. {
  213. int abs_x = old_x + distance + frm->GetAbsX() - doc->GetVisualDevice()->GetRenderingViewX();
  214. int abs_y = old_y + frm->GetAbsY() - doc->GetVisualDevice()->GetRenderingViewY();
  215. FramesDocument* top_doc = frm->GetTopFramesDoc();
  216. FramesDocElm* top_frm = top_doc->GetDocManager()->GetFrame();
  217. FramesDocElm* targetframe = FindFrame(abs_x, abs_y, top_doc, top_frm);
  218. if (targetframe)
  219. {
  220. FramesDocument* frm_doc = (FramesDocument*) targetframe->GetCurrentDoc();
  221. if (frm_doc && frm_doc->GetDocumentEdit())
  222. {
  223. // FIX: correct coordinates!!
  224. int x = 10;
  225. int y = 10;
  226. GetCaretManager()->Place(x, y, x, y, TRUE, TRUE);
  227. frm_doc->GetDocumentEdit()->SetFocus(FOCUS_REASON_OTHER);
  228. }
  229. }
  230. }
  231. }
  232. #endif
  233. Place(moved_helm, moved_ofs);
  234. //UpdatePos();
  235. }
  236. }
  237. }
  238. void OpDocumentEditCaret::MoveSpatialVertical(BOOL down)
  239. {
  240. DEBUG_CHECKER(TRUE);
  241. #ifdef DOCUMENTEDIT_SPATIAL_FRAMES_FOCUS
  242. // Search for another frame
  243. FramesDocument* doc = m_edit->GetDoc();
  244. FramesDocElm* frm = doc->GetDocManager()->GetFrame();
  245. if (frm)
  246. {
  247. int height = frm->GetHeight();
  248. int abs_x = frm->GetAbsX() - doc->GetVisualDevice()->GetRenderingViewX();
  249. int abs_y = frm->GetAbsY() - doc->GetVisualDevice()->GetRenderingViewY() + (down ? height + 10 : -10);
  250. FramesDocument* top_doc = frm->GetTopFramesDoc();
  251. FramesDocElm* top_frm = top_doc->GetDocManager()->GetFrame();
  252. FramesDocElm* targetframe = FindFrame(abs_x, abs_y, top_doc, top_frm);
  253. if (targetframe)
  254. {
  255. FramesDocument* frm_doc = (FramesDocument*) targetframe->GetCurrentDoc();
  256. if (frm_doc && frm_doc->GetDocumentEdit())
  257. {
  258. // FIX: correct coordinates!!
  259. int x = 10;
  260. int y = 10;
  261. GetCaretManager()->Place(x, y, x, y, FALSE, TRUE);
  262. frm_doc->GetDocumentEdit()->SetFocus(FOCUS_REASON_OTHER);
  263. }
  264. }
  265. }
  266. #endif
  267. }
  268. void OpDocumentEditCaret::SetRemoveWhenMoveIfUntouched()
  269. {
  270. DEBUG_CHECKER(TRUE);
  271. if(!m_edit->m_undo_stack.GetBeginCount())
  272. {
  273. m_remove_when_move_elm = GetElement();
  274. m_remove_when_move_id = m_edit->m_undo_stack.GetBeginCountCalls();
  275. }
  276. }
  277. void OpDocumentEditCaret::DeleteRemoveWhenMoveIfUntouched(HTML_Element *new_helm)
  278. {
  279. DEBUG_CHECKER(TRUE);
  280. if(m_remove_when_move_elm &&
  281. new_helm != m_remove_when_move_elm &&
  282. m_remove_when_move_id == m_edit->m_undo_stack.GetBeginCountCalls() &&
  283. !m_remove_when_move_elm->GetTextContentLength() &&
  284. !m_edit->m_undo_stack.GetBeginCount()
  285. )
  286. {
  287. HTML_Element *to_delete = m_remove_when_move_elm;
  288. HTML_Element *parent = to_delete->ParentActual();
  289. m_remove_when_move_elm = NULL;
  290. m_edit->BeginChange(parent, CHANGE_FLAGS_ALLOW_APPEND | CHANGE_FLAGS_USER_INVISIBLE);
  291. m_edit->DeleteElement(to_delete);
  292. m_edit->EndChange(parent,parent,parent,FALSE,TIDY_LEVEL_MINIMAL);
  293. }
  294. if(m_remove_when_move_elm != new_helm)
  295. m_remove_when_move_elm = NULL;
  296. }
  297. BOOL OpDocumentEditCaret::IsAtRemoveWhenMoveIfUntounced()
  298. {
  299. DEBUG_CHECKER(TRUE);
  300. return m_remove_when_move_elm &&
  301. GetElement() == m_remove_when_move_elm &&
  302. m_edit->m_undo_stack.GetBeginCountCalls() == m_remove_when_move_id;
  303. }
  304. void OpDocumentEditCaret::CleanTemporaryCaretTextElement(BOOL new_is_created)
  305. {
  306. HTML_Element* current_temp_elm = m_temp_caret_text_elm.GetElm();
  307. if (current_temp_elm && (new_is_created || GetElement() != current_temp_elm))
  308. {
  309. m_temp_caret_text_elm.Reset();
  310. if (current_temp_elm->GetTextContentLength() == 0) // Not modified. May be removed
  311. {
  312. current_temp_elm->OutSafe(m_edit->GetDoc());
  313. m_edit->DeleteElement(current_temp_elm);
  314. }
  315. }
  316. }
  317. HTML_Element* OpDocumentEditCaret::ConvertTemporaryCaretTextElementToBR()
  318. {
  319. HTML_Element* current_temp_elm = m_temp_caret_text_elm.GetElm();
  320. enum
  321. {
  322. INSERT_AFTER,
  323. INSERT_BEFORE,
  324. INSERT_UNDER
  325. } insert_type;
  326. HTML_Element* orientation_point;
  327. if (current_temp_elm)
  328. {
  329. if (orientation_point = current_temp_elm->Suc())
  330. {
  331. insert_type = INSERT_BEFORE;
  332. }
  333. else if (orientation_point = current_temp_elm->Pred())
  334. {
  335. insert_type = INSERT_AFTER;
  336. }
  337. else
  338. {
  339. orientation_point = current_temp_elm->Parent();
  340. insert_type = INSERT_UNDER;
  341. }
  342. if (current_temp_elm->GetTextContentLength() == 0) // Not modified. May be removed
  343. {
  344. HTML_Element* br_elm = m_edit->NewElement(Markup::HTE_BR, TRUE);
  345. if (br_elm && orientation_point)
  346. {
  347. if (insert_type == INSERT_AFTER)
  348. br_elm->FollowSafe(m_edit->GetDoc(), orientation_point);
  349. else if (insert_type == INSERT_BEFORE)
  350. br_elm->PrecedeSafe(m_edit->GetDoc(), orientation_point);
  351. else
  352. br_elm->UnderSafe(m_edit->GetDoc(), orientation_point);
  353. m_temp_caret_text_elm.Reset();
  354. current_temp_elm->OutSafe(m_edit->GetDoc());
  355. m_edit->DeleteElement(current_temp_elm);
  356. Set(br_elm, 0);
  357. }
  358. return br_elm;
  359. }
  360. }
  361. return m_temp_caret_text_elm.GetElm();
  362. }
  363. BOOL OpDocumentEditCaret::IsUnmodifiedTemporaryCaretTextElement()
  364. {
  365. return GetElement() && GetElement() == m_temp_caret_text_elm.GetElm() && GetElement()->GetTextContentLength() == 0;
  366. }
  367. HTML_Element* OpDocumentEditCaret::CreateTemporaryCaretHelm(HTML_Element *parent, HTML_Element *after_me, BOOL text)
  368. {
  369. DEBUG_CHECKER(TRUE);
  370. if(!parent && !after_me)
  371. {
  372. OP_ASSERT(FALSE);
  373. return NULL;
  374. }
  375. if(!parent)
  376. parent = after_me->Parent();
  377. if(!parent || !m_edit->m_undo_stack.IsValidAsChangeElm(parent))
  378. return NULL;
  379. if (after_me && (after_me == m_temp_caret_text_elm.GetElm() ||
  380. (after_me->IsMatchingType(Markup::HTE_BR, NS_HTML) && m_edit->GetInsertedAutomatically(after_me))))
  381. // No need to create another temp. elm. It would be even wrong if we did.
  382. return after_me;
  383. // If we're already on a temporary text-element that should be removed when caret moves if logtree
  384. // is untouched, then we want to keep it "untouched" even after adding new element
  385. BOOL is_at_untouched_remove = IsAtRemoveWhenMoveIfUntounced();
  386. HTML_Element *new_helm;
  387. if (!text)
  388. new_helm = m_edit->NewElement(HE_BR, TRUE);
  389. else
  390. {
  391. CleanTemporaryCaretTextElement(TRUE);
  392. new_helm = m_edit->NewTextElement(UNI_L(""), 0);
  393. m_temp_caret_text_elm.SetElm(new_helm);
  394. }
  395. if(!new_helm)
  396. {
  397. m_edit->ReportOOM();
  398. return NULL;
  399. }
  400. if(OpStatus::IsError(m_edit->BeginChange(parent, CHANGE_FLAGS_ALLOW_APPEND | CHANGE_FLAGS_USER_INVISIBLE)))
  401. {
  402. m_edit->DeleteElement(new_helm);
  403. return NULL;
  404. }
  405. if(after_me)
  406. new_helm->FollowSafe(m_edit->GetDoc(),after_me);
  407. else
  408. {
  409. if(parent->FirstChild())
  410. new_helm->PrecedeSafe(m_edit->GetDoc(),parent->FirstChild());
  411. else
  412. new_helm->UnderSafe(m_edit->GetDoc(),parent);
  413. }
  414. new_helm->MarkExtraDirty(m_edit->GetDoc());
  415. m_edit->EndChange(parent,new_helm,new_helm,FALSE,TIDY_LEVEL_MINIMAL);
  416. if(is_at_untouched_remove)
  417. SetRemoveWhenMoveIfUntouched();
  418. return new_helm;
  419. }
  420. void OpDocumentEditCaret::MakeRoomAndMoveCaret(HTML_Element* new_helm, int new_ofs, BOOL forward)
  421. {
  422. if(m_edit->MakeSureHasValidEdgeCaretPos(GetElement(), NULL, forward))
  423. {
  424. if (m_edit->GetOneStepBeside(forward, GetElement(), GetOffset(), new_helm, new_ofs))
  425. {
  426. GetCaretManager()->Place(new_helm, new_ofs, FALSE, TRUE, TRUE);
  427. SetRemoveWhenMoveIfUntouched();
  428. }
  429. }
  430. }
  431. BOOL OpDocumentEditCaret::IsAtStartOrEndOfElement(BOOL start)
  432. {
  433. DEBUG_CHECKER(TRUE);
  434. if (!GetElement() || !GetElement()->GetLayoutBox())
  435. return TRUE;
  436. if (!GetElement()->GetLayoutBox()->IsTextBox())
  437. return (start && GetOffset() == 0) || (!start && GetOffset() == 1);
  438. OpDocumentEditWordIterator iter(GetElement(),m_edit);
  439. if(OpStatus::IsError(iter.GetStatus()) || !iter.HasUnCollapsedChar())
  440. return TRUE;
  441. int dummy;
  442. return !iter.GetValidCaretOfsFrom(GetOffset(),dummy,!start);
  443. }
  444. BOOL OpDocumentEditCaret::IsAtStartOrEndOfBlock(BOOL start, BOOL *is_at_edge_to_child_container)
  445. {
  446. if (is_at_edge_to_child_container)
  447. *is_at_edge_to_child_container = FALSE;
  448. DEBUG_CHECKER(TRUE);
  449. if (!GetElement())
  450. return TRUE;
  451. HTML_Element* nearest_helm = NULL;
  452. int nearest_ofs;
  453. if(!m_edit->GetOneStepBeside(!start,GetElement(),GetOffset(),nearest_helm,nearest_ofs))
  454. return TRUE;
  455. if (!m_edit->IsFriends(GetElement(), nearest_helm, FALSE, TRUE, TRUE))
  456. {
  457. if (GetElement()->Parent()->IsAncestorOf(nearest_helm))
  458. {
  459. if (is_at_edge_to_child_container)
  460. *is_at_edge_to_child_container = TRUE;
  461. return FALSE;
  462. }
  463. return TRUE;
  464. }
  465. return FALSE;
  466. }
  467. void OpDocumentEditCaret::Place(const SelectionBoundaryPoint& point)
  468. {
  469. DEBUG_CHECKER(TRUE);
  470. HTML_Element *point_helm = point.GetElement();
  471. if (m_edit->IsElementValidForCaret(point_helm,TRUE,FALSE,TRUE))
  472. {
  473. int ofs = point.GetElementCharacterOffset();
  474. Place(point_helm, ofs, FALSE, FALSE);
  475. }
  476. else
  477. {
  478. // Convert the parent element and child offset to element and offset 0/1
  479. int ofs;
  480. HTML_Element* tmp;
  481. TextSelection::ConvertPointToOldStyle(point, tmp, ofs);
  482. if (tmp)
  483. Place(tmp, ofs);
  484. else
  485. {
  486. /* Looks like the selection point is an element a caret normally can not be placed in.
  487. * However if it's not a standalone element, init the caret in it to allow placing
  488. * the caret in the empty selection.
  489. */
  490. if (!m_edit->IsStandaloneElement(point_helm))
  491. Init(TRUE, point_helm);
  492. }
  493. }
  494. UpdateWantedX();
  495. }
  496. BOOL OpDocumentEditCaret::FixAndCheckCaretMovement(HTML_Element*& helm, int& ofs, BOOL allow_snap, BOOL keep_within_current_context)
  497. {
  498. if(allow_snap)
  499. {
  500. HTML_Element *valid_helm = NULL;
  501. m_edit->GetValidCaretPosFrom(helm,ofs,valid_helm,ofs);
  502. if(valid_helm != helm)
  503. {
  504. if(helm->Type() == HE_TEXT)
  505. {
  506. if(!valid_helm || !m_edit->IsFriends(helm,valid_helm,TRUE,TRUE))
  507. {
  508. m_edit->SetElementText(helm,UNI_L(""));
  509. valid_helm = helm;
  510. ofs = 0;
  511. }
  512. }
  513. }
  514. if (!valid_helm)
  515. {
  516. StopBlink();
  517. return FALSE;
  518. }
  519. helm = valid_helm;
  520. if(helm->Type() == HE_TEXT && helm->GetTextContentLength())
  521. {
  522. OpDocumentEditWordIterator iter(helm,m_edit);
  523. if(OpStatus::IsSuccess(iter.GetStatus()) && !iter.UnCollapsedCount())
  524. {
  525. m_edit->SetElementText(helm,UNI_L(""));
  526. ofs = 0;
  527. }
  528. }
  529. }
  530. if (!IsElementEditable(helm))
  531. return FALSE;
  532. if (keep_within_current_context && !m_edit->m_body_is_root && GetElement())
  533. {
  534. // Check if the new place is still in the same contentEditable
  535. HTML_Element* ec1 = m_edit->GetEditableContainer(GetElement());
  536. HTML_Element* ec2 = m_edit->GetEditableContainer(helm);
  537. if (ec1 != ec2)
  538. return FALSE;
  539. }
  540. return TRUE;
  541. }
  542. BOOL OpDocumentEditCaret::IsElementEditable(HTML_Element* helm)
  543. {
  544. DEBUG_CHECKER(TRUE);
  545. if (!helm || !(helm->IsIncludedActual() || helm->GetInserted() == HE_INSERTED_BY_IME))
  546. return FALSE;
  547. HTML_Element* body = m_edit->GetBody();
  548. return (m_edit->m_body_is_root || helm->IsContentEditable(TRUE)) && body && body->IsAncestorOf(helm);
  549. }
  550. void OpDocumentEditCaret::Place(INT32 x, INT32 y, BOOL remember_x, BOOL enter_all, BOOL search_whole_viewport)
  551. {
  552. AffinePos ctm; ctm.SetTranslation(0, 0);
  553. m_edit->CheckLogTreeChanged();
  554. if (GetCaretManager()->PlaceAtCoordinates(ctm, x, y, remember_x, enter_all, search_whole_viewport))
  555. {
  556. m_edit->ClearPendingStyles();
  557. m_edit->m_layout_modifier.Unactivate();
  558. }
  559. }
  560. void OpDocumentEditCaret::Place(OpWindowCommander::CaretMovementDirection place)
  561. {
  562. m_edit->CheckLogTreeChanged();
  563. HTML_Element *containing_element = m_edit->GetDoc()->GetCaret()->GetContainingElement(GetElement());
  564. if (place == OpWindowCommander::CARET_DOCUMENT_HOME || place == OpWindowCommander::CARET_DOCUMENT_END)
  565. {
  566. // For start and end, we want to use the editable container.
  567. containing_element = m_edit->GetEditableContainer(containing_element);
  568. }
  569. if (!containing_element)
  570. return;
  571. GetCaretManager()->MoveCaret(place, containing_element, m_edit->GetEditableContainer(GetElement()));
  572. m_edit->ClearPendingStyles();
  573. m_edit->m_layout_modifier.Unactivate();
  574. }
  575. void OpDocumentEditCaret::Set(HTML_Element* helm, int ofs, BOOL prefer_first, BOOL remember_x /* = TRUE*/)
  576. {
  577. DEBUG_CHECKER(TRUE);
  578. // OP_ASSERT(!helm || helm->Type() == HE_TEXT || (helm->Type() == HE_BR && ofs == 0));
  579. // OP_ASSERT(!helm || m_edit->GetRoot()->IsAncestorOf(helm));
  580. #ifdef _DOCEDIT_DEBUG
  581. if (!helm)
  582. {
  583. int stop = 0; (void) stop;
  584. }
  585. #endif
  586. #ifdef INTERNAL_SPELLCHECK_SUPPORT
  587. m_edit->OnBeforeNewCaretPos(helm, ofs);
  588. #endif
  589. if (helm)
  590. {
  591. if (ofs == 1 && helm->Type() != HE_TEXT && !helm->IsMatchingType(HE_BR, NS_HTML))
  592. {
  593. // The old style method of saying "after helm". Translate...
  594. if (HTML_Element* parent = helm->ParentActual())
  595. {
  596. int helm_offset = 0;
  597. HTML_Element* pred = helm->PredActual();
  598. while (pred)
  599. {
  600. helm_offset++;
  601. pred = pred->PredActual();
  602. }
  603. helm = parent;
  604. ofs = helm_offset + 1;
  605. }
  606. }
  607. SelectionBoundaryPoint caret_point(helm, ofs);
  608. caret_point.SetBindDirection(prefer_first ? SelectionBoundaryPoint::BIND_BACKWARD : SelectionBoundaryPoint::BIND_FORWARD);
  609. #ifdef DRAG_SUPPORT
  610. CaretPainter* caret_painter = m_edit->GetDoc()->GetCaretPainter();
  611. if (caret_painter->IsDragCaret())
  612. caret_painter->SetDragCaretPoint(caret_point);
  613. else
  614. #endif // DRAG_SUPPORT
  615. m_edit->GetDoc()->SetSelection(&caret_point, &caret_point, FALSE, !remember_x);
  616. }
  617. #ifdef DRAG_SUPPORT
  618. else
  619. {
  620. CaretPainter* caret_painter = m_edit->GetDoc()->GetCaretPainter();
  621. if (caret_painter->IsDragCaret())
  622. caret_painter->GetDragCaretPoint().Reset();
  623. }
  624. #endif // DRAG_SUPPORT
  625. HTML_Element* temp_caret_elm = m_temp_caret_text_elm.GetElm();
  626. if (temp_caret_elm && temp_caret_elm != helm)
  627. {
  628. // It would be better to remove it complately but 'no, can't do sir. Someone might be using the old caret's HTML_Element pointer.'
  629. m_temp_caret_text_elm.Reset();
  630. StoreRealCaretPlacement(NULL, 0);
  631. }
  632. m_parent_candidate = NULL;
  633. }
  634. void OpDocumentEditCaret::StoreRealCaretPlacement(HTML_Element* helm, int ofs)
  635. {
  636. m_real_caret_elm.SetElm(helm);
  637. m_real_caret_elm_off = ofs;
  638. }
  639. void OpDocumentEditCaret::Set(HTML_Element* helm, int ofs, int x, int y, int height)
  640. {
  641. DEBUG_CHECKER(TRUE);
  642. Set(helm , ofs);
  643. RestartBlink();
  644. m_edit->GetDoc()->GetCaretPainter()->Invalidate();
  645. m_edit->GetDoc()->GetCaretPainter()->SetX(x);
  646. m_edit->GetDoc()->GetCaretPainter()->SetPaintX(x);
  647. m_edit->GetDoc()->GetCaretPainter()->SetY(y);
  648. m_edit->GetDoc()->GetCaretPainter()->SetLineY(y);
  649. m_edit->GetDoc()->GetCaretPainter()->SetHeight(height);
  650. m_edit->GetDoc()->GetCaretPainter()->SetLineHeight(height);
  651. m_edit->GetDoc()->GetCaretPainter()->SetWantedX(x);
  652. m_edit->GetDoc()->GetCaretPainter()->Invalidate();
  653. }
  654. void OpDocumentEditCaret::StickToPreceding()
  655. {
  656. DEBUG_CHECKER(TRUE);
  657. if (GetElement()->Type() == HE_TEXT && GetElement()->GetTextContentLength() == 0)
  658. return; // Stay inside dummyelements
  659. if (GetOffset() == 0)
  660. {
  661. HTML_Element* tmp = m_edit->FindElementBeforeOfType(GetElement(), HE_TEXT, TRUE);
  662. if (tmp)
  663. {
  664. if (m_edit->IsFriends(tmp, GetElement()))
  665. {
  666. // Check if it is possible to place in this element.
  667. int new_ofs;
  668. OpDocumentEditWordIterator iter(tmp,m_edit);
  669. if(OpStatus::IsSuccess(iter.GetStatus()) && iter.GetLastValidCaretOfs(new_ofs))
  670. Place(tmp, new_ofs);
  671. }
  672. }
  673. }
  674. }
  675. void OpDocumentEditCaret::StickToDummy()
  676. {
  677. DEBUG_CHECKER(TRUE);
  678. if (GetElement()->Type() == HE_TEXT && GetElement()->GetTextContentLength() == GetOffset())
  679. {
  680. HTML_Element* tmp = m_edit->FindElementAfterOfType(GetElement(), HE_TEXT, TRUE);
  681. if (tmp && tmp->GetTextContentLength() == 0)
  682. {
  683. if (m_edit->IsFriends(tmp, GetElement()))
  684. {
  685. Place(tmp, 0);
  686. }
  687. }
  688. }
  689. }
  690. void OpDocumentEditCaret::UpdateWantedX()
  691. {
  692. m_edit->GetDoc()->GetCaretPainter()->UpdateWantedX();
  693. }
  694. void OpDocumentEditCaret::SetOverstrike(BOOL overstrike)
  695. {
  696. m_edit->GetDoc()->GetCaretPainter()->SetOverstrike(overstrike);
  697. }
  698. void OpDocumentEditCaret::BlinkNow()
  699. {
  700. m_edit->GetDoc()->GetCaretPainter()->BlinkNow();
  701. }
  702. void OpDocumentEditCaret::StopBlink()
  703. {
  704. m_edit->GetDoc()->GetCaretPainter()->StopBlink();
  705. }
  706. void OpDocumentEditCaret::LockUpdatePos(BOOL lock, BOOL process_update_pos)
  707. {
  708. m_edit->GetDoc()->GetCaretPainter()->LockUpdatePos(lock, process_update_pos);
  709. }
  710. BOOL OpDocumentEditCaret::UpdatePos(BOOL prefer_first, BOOL create_line_if_empty)
  711. {
  712. if (!GetCaretManager()->GetElement())
  713. {
  714. PlaceFirst(m_parent_candidate, create_line_if_empty);
  715. if (!GetElement())
  716. return FALSE;
  717. }
  718. BOOL changed = m_edit->GetDoc()->GetCaretPainter()->UpdatePos();
  719. return changed;
  720. }
  721. void OpDocumentEditCaret::OnUpdatePosDone()
  722. {
  723. if (m_edit->IsFocused())
  724. {
  725. VisualDevice* vis_dev = m_edit->GetDoc()->GetVisualDevice();
  726. OpRect rect(m_edit->GetDoc()->GetCaretPainter()->GetX() - vis_dev->GetRenderingViewX(), m_edit->GetDoc()->GetCaretPainter()->GetY() - vis_dev->GetRenderingViewY(), m_edit->GetDoc()->GetCaretPainter()->GetWidth(), m_edit->GetDoc()->GetCaretPainter()->GetHeight());
  727. rect = vis_dev->ScaleToScreen(rect);
  728. rect = vis_dev->OffsetToContainer(rect);
  729. vis_dev->GetOpView()->OnHighlightRectChanged(rect);
  730. vis_dev->GetOpView()->OnCaretPosChanged(rect);
  731. }
  732. OpDocumentEditListener* listener = m_edit->GetListener();
  733. if (listener)
  734. listener->OnCaretMoved();
  735. }
  736. OpRect OpDocumentEditCaret::GetCaretRectInDocument() const
  737. {
  738. return m_edit->GetDoc()->GetCaretPainter()->GetCaretRectInDocument();
  739. }
  740. void OpDocumentEditCaret::RestartBlink()
  741. {
  742. StopBlink();
  743. if (!m_edit->IsFocused())
  744. return;
  745. m_edit->GetDoc()->GetCaretPainter()->RestartBlink();
  746. }
  747. #endif // DOCUMENT_EDIT_SUPPORT