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.

OpDocumentEditUndoRedo.cpp 28KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  1. /* -*- Mode: c++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
  2. *
  3. * Copyright (C) 1995-2004 Opera Software AS. All rights reserved.
  4. *
  5. * This file is part of the Opera web browser. It may not be distributed
  6. * under any circumstances.
  7. */
  8. #include "core/pch.h"
  9. #include "modules/util/simset.h"
  10. #include "modules/documentedit/OpDocumentEdit.h"
  11. #include "modules/documentedit/OpDocumentEditUtils.h"
  12. #include "modules/documentedit/OpDocumentEditUndoRedo.h"
  13. #include "modules/dom/domutils.h"
  14. #ifdef DOCUMENT_EDIT_SUPPORT
  15. // #define DOCEDIT_PRINT_LOGS
  16. // == OpDocumentEditUndoRedoAutoGroup ==================================================
  17. OpDocumentEditUndoRedoAutoGroup::OpDocumentEditUndoRedoAutoGroup(OpDocumentEditUndoRedoStack* stack)
  18. : stack(stack)
  19. {
  20. stack->BeginGroup();
  21. }
  22. OpDocumentEditUndoRedoAutoGroup::~OpDocumentEditUndoRedoAutoGroup()
  23. {
  24. stack->EndGroup();
  25. }
  26. // == OpDocumentEditDisableUndoRegistrationAuto ==================================================
  27. OpDocumentEditDisableUndoRegistrationAuto::OpDocumentEditDisableUndoRegistrationAuto(OpDocumentEditUndoRedoStack* stack)
  28. : stack(stack)
  29. {
  30. OP_ASSERT(stack);
  31. stack->DisableChangeRegistration();
  32. }
  33. OpDocumentEditDisableUndoRegistrationAuto::~OpDocumentEditDisableUndoRegistrationAuto()
  34. {
  35. stack->EnableChangeRegistration();
  36. }
  37. // == OpDocumentEditUndoRedoEvent ==================================================
  38. OpDocumentEditUndoRedoEvent::OpDocumentEditUndoRedoEvent(
  39. OpDocumentEdit *edit
  40. )
  41. : caret_elm_nr_before(0)
  42. , caret_ofs_before(0)
  43. , caret_elm_nr_after(0)
  44. , caret_ofs_after(0)
  45. , elm_nr(0)
  46. , changed_elm_nr_before(-1)
  47. , changed_elm_nr_after(-1)
  48. , bytes_used(0)
  49. , element_changed(NULL)
  50. , tree_inserter(this)
  51. , type(UNDO_REDO_TYPE_UNKNOWN)
  52. , user_invisible(FALSE)
  53. , is_ended(FALSE)
  54. , m_edit(edit)
  55. , stage(UNDO)
  56. , runtime(NULL)
  57. {
  58. DEBUG_CHECKER_CONSTRUCTOR();
  59. }
  60. OpDocumentEditUndoRedoEvent::~OpDocumentEditUndoRedoEvent()
  61. {
  62. DEBUG_CHECKER(TRUE);
  63. while(OpDocumentEditUndoRedoEvent* merged = static_cast<OpDocumentEditUndoRedoEvent*>(First()))
  64. OP_DELETE(merged);
  65. Out();
  66. // No clone in below cases
  67. if ((type != UNDO_REDO_ELEMENT_INSERT || stage != UNDO) &&
  68. (type != UNDO_REDO_ELEMENT_REMOVE || stage != REDO) &&
  69. type != UNDO_REDO_CARET_PLACEMENT_BEGIN &&
  70. type != UNDO_REDO_CARET_PLACEMENT_END)
  71. {
  72. if (element_changed)
  73. {
  74. Unprotect(element_changed);
  75. OpDocumentEdit::DeleteElement(element_changed, m_edit, FALSE);
  76. }
  77. }
  78. }
  79. OP_STATUS OpDocumentEditUndoRedoEvent::Protect(HTML_Element* element)
  80. {
  81. DEBUG_CHECKER(TRUE);
  82. DOM_Object *node;
  83. DOM_Environment *environment = m_edit->GetDoc()->GetDOMEnvironment();
  84. if (!environment)
  85. return OpStatus::OK;
  86. RETURN_IF_ERROR(environment->ConstructNode(node, element));
  87. runtime = environment->GetRuntime();
  88. return runtime->Protect(DOM_Utils::GetES_Object(node)) ? OpStatus::OK : OpStatus::ERR_NO_MEMORY;
  89. }
  90. void OpDocumentEditUndoRedoEvent::Unprotect(HTML_Element* element)
  91. {
  92. DEBUG_CHECKER(TRUE);
  93. if (runtime)
  94. if (DOM_Object *node = element->GetESElement())
  95. runtime->Unprotect(DOM_Utils::GetES_Object(node));
  96. }
  97. #if defined(DOCEDIT_PRINT_LOGS) && defined(_DOCEDIT_DEBUG)
  98. #define DOCEDIT_DBG(x) dbg_printf x
  99. #else
  100. #define DOCEDIT_DBG(x)
  101. #endif
  102. OP_STATUS OpDocumentEditUndoRedoEvent::BeginChange(HTML_Element* containing_elm, HTML_Element* element_changed, int type)
  103. {
  104. DEBUG_CHECKER(TRUE);
  105. this->type = type;
  106. DOCEDIT_DBG(("EVENT %d, ELM %d\n", type, element_changed ? element_changed->Type() : -1));
  107. if (type != UNDO_REDO_CARET_PLACEMENT_BEGIN && type != UNDO_REDO_CARET_PLACEMENT_END)
  108. {
  109. if (type != UNDO_REDO_ELEMENT_INSERT)
  110. {
  111. changed_elm_nr_before = GetNumberOfElement(element_changed);
  112. DOCEDIT_DBG(("BEFORE %d, ", changed_elm_nr_before));
  113. this->element_changed = CreateClone(element_changed, TRUE);
  114. RETURN_IF_ERROR(Protect(this->element_changed));
  115. if(!this->element_changed)
  116. return OpStatus::ERR_NO_MEMORY;
  117. tree_inserter.FindPlaceMarker(element_changed);
  118. }
  119. else
  120. {
  121. this->element_changed = element_changed;
  122. changed_elm_nr_before = -1;
  123. }
  124. }
  125. else
  126. {
  127. elm_nr = GetNumberOfElement(containing_elm);
  128. caret_elm_nr_before = GetNumberOfElement(m_edit->m_caret.GetElement());
  129. caret_ofs_before = m_edit->m_caret.GetOffset();
  130. }
  131. UpdateBytesUsed();
  132. return OpStatus::OK;
  133. }
  134. OP_STATUS OpDocumentEditUndoRedoEvent::EndChange(HTML_Element* containing_elm)
  135. {
  136. DEBUG_CHECKER(TRUE);
  137. OP_ASSERT(!is_ended);
  138. if (type != UNDO_REDO_CARET_PLACEMENT_BEGIN && type != UNDO_REDO_CARET_PLACEMENT_END)
  139. {
  140. if (type == UNDO_REDO_ELEMENT_INSERT)
  141. changed_elm_nr_after = GetNumberOfElement(element_changed);
  142. else
  143. {
  144. changed_elm_nr_after = (type == UNDO_REDO_ELEMENT_REMOVE) ? -1 : changed_elm_nr_before;
  145. }
  146. }
  147. else
  148. {
  149. caret_elm_nr_after = GetNumberOfElement(m_edit->m_caret.GetElement());
  150. caret_ofs_after = m_edit->m_caret.GetOffset();
  151. }
  152. DOCEDIT_DBG(("END %d\n", changed_elm_nr_after));
  153. is_ended = TRUE;
  154. return OpStatus::OK;
  155. }
  156. OP_STATUS OpDocumentEditUndoRedoEvent::Clone(HTML_Element* target, HTML_Element* source, BOOL deep)
  157. {
  158. DEBUG_CHECKER(TRUE);
  159. FramesDocument* fdoc = m_edit->GetDoc();
  160. HLDocProfile *hld_profile = fdoc->GetHLDocProfile();
  161. if (deep)
  162. {
  163. // Delete children
  164. while(target->FirstChild())
  165. m_edit->DeleteElement(target->FirstChild(), FALSE);
  166. // Clone with children
  167. RETURN_IF_ERROR(target->DeepClone(hld_profile, source));
  168. target->MarkExtraDirty(fdoc);
  169. target->Parent()->MarkDirty(fdoc, FALSE, TRUE);
  170. }
  171. else
  172. {
  173. // Create new clone
  174. HTML_Element* copy = m_edit->NewCopyOfElement(source);
  175. if (!copy)
  176. return OpStatus::ERR_NO_MEMORY;
  177. copy->PrecedeSafe(fdoc, target);
  178. copy->MarkExtraDirty(fdoc);
  179. copy->SetEndTagFound();
  180. // Move children to the new clone and remove the source.
  181. while(target->FirstChild())
  182. {
  183. HTML_Element* move = target->FirstChild();
  184. move->OutSafe(fdoc, FALSE);
  185. move->UnderSafe(fdoc, copy);
  186. move->MarkExtraDirty(fdoc);
  187. }
  188. if (copy->Type() == HE_BODY)
  189. hld_profile->SetBodyElm(copy);
  190. m_edit->DeleteElement(target, FALSE);
  191. m_edit->GetBody()->MarkDirty(fdoc, FALSE, TRUE);
  192. }
  193. return OpStatus::OK;
  194. }
  195. void OpDocumentEditUndoRedoEvent::Action(int type)
  196. {
  197. switch (type)
  198. {
  199. case UNDO_REDO_ELEMENT_INSERT:
  200. {
  201. if (element_changed)
  202. {
  203. Unprotect(element_changed);
  204. /* If a stage is UNDO this means it's in fact REMOVE event so nr before is only valid for it.
  205. * For INSERT REDO nr after is only valid
  206. */
  207. INT32 pos = stage == UNDO ? changed_elm_nr_before : changed_elm_nr_after;
  208. DOCEDIT_DBG(("INSERT, ELM %d, POS %d, ", element_changed->Type(), pos));
  209. tree_inserter.Insert(element_changed, m_edit->GetDoc(), pos);
  210. #ifdef _DOCEDIT_DEBUG
  211. INT32 after = GetNumberOfElement(element_changed);
  212. DOCEDIT_DBG(("AFTER %d\n", after));
  213. OP_ASSERT((changed_elm_nr_before == after && stage == UNDO) || (changed_elm_nr_after == after && stage == REDO));
  214. #endif // _DOCEDIT_DEBUG
  215. }
  216. break;
  217. }
  218. case UNDO_REDO_ELEMENT_REMOVE:
  219. {
  220. INT32 elm_nr = (stage == UNDO) ? changed_elm_nr_after : changed_elm_nr_before;
  221. HTML_Element* elem_to_remove = GetElementOfNumber(elm_nr);
  222. if (elem_to_remove)
  223. {
  224. DOCEDIT_DBG(("REMOVE, ELM %d, POS %d\n", elem_to_remove->Type(), elm_nr));
  225. tree_inserter.FindPlaceMarker(elem_to_remove);
  226. element_changed = CreateClone(elem_to_remove, TRUE);
  227. if (!element_changed)
  228. m_edit->ReportOOM();
  229. Protect(element_changed);
  230. m_edit->DeleteElement(elem_to_remove);
  231. }
  232. else
  233. element_changed = NULL;
  234. break;
  235. }
  236. case UNDO_REDO_ELEMENT_CHANGE:
  237. case UNDO_REDO_ELEMENT_REVERT_CHANGE:
  238. {
  239. OP_ASSERT(changed_elm_nr_before == changed_elm_nr_after);
  240. HTML_Element* changed = GetElementOfNumber(changed_elm_nr_before);
  241. if (changed)
  242. {
  243. OP_ASSERT(changed->Type() == element_changed->Type());
  244. DOCEDIT_DBG(("CHANGE, ELM %d, POS %d, ", changed->Type(), changed_elm_nr_before));
  245. HTML_Element* to_remove = changed;
  246. changed = CreateClone(changed, TRUE);
  247. if (!changed)
  248. m_edit->ReportOOM();
  249. Protect(changed);
  250. m_edit->DeleteElement(to_remove, FALSE);
  251. Unprotect(element_changed);
  252. tree_inserter.Insert(element_changed, m_edit->GetDoc(), changed_elm_nr_before);
  253. #ifdef _DOCEDIT_DEBUG
  254. INT32 after = GetNumberOfElement(element_changed);
  255. DOCEDIT_DBG(("AFTER %d\n", after));
  256. OP_ASSERT(changed_elm_nr_before == after);
  257. #endif // _DOCEDIT_DEBUG
  258. }
  259. element_changed = changed;
  260. break;
  261. }
  262. case UNDO_REDO_CARET_PLACEMENT_BEGIN:
  263. case UNDO_REDO_CARET_PLACEMENT_END:
  264. DOCEDIT_DBG(("CARET\n"));
  265. break;
  266. default:
  267. OP_ASSERT(!"Unknown event type");
  268. return;
  269. }
  270. }
  271. void OpDocumentEditUndoRedoEvent::Undo()
  272. {
  273. DEBUG_CHECKER(TRUE);
  274. OP_ASSERT(is_ended);
  275. OpDocumentEditUndoRedoEvent* merged = static_cast<OpDocumentEditUndoRedoEvent*>(Last());
  276. while (merged)
  277. {
  278. OpDocumentEditUndoRedoEvent* pred_merged = static_cast<OpDocumentEditUndoRedoEvent*>(merged->Pred());
  279. merged->Undo();
  280. merged = pred_merged;
  281. }
  282. Action(~type);
  283. stage = REDO;
  284. if (type == UNDO_REDO_CARET_PLACEMENT_BEGIN)
  285. {
  286. m_edit->ReflowAndUpdate();
  287. HTML_Element* new_caret_elm = GetElementOfNumber(caret_elm_nr_before);
  288. if (new_caret_elm)
  289. m_edit->m_caret.Place(new_caret_elm, caret_ofs_before);
  290. }
  291. }
  292. void OpDocumentEditUndoRedoEvent::Redo()
  293. {
  294. DEBUG_CHECKER(TRUE);
  295. OP_ASSERT(is_ended);
  296. Action(type);
  297. stage = UNDO;
  298. if (type == UNDO_REDO_CARET_PLACEMENT_END)
  299. {
  300. m_edit->ReflowAndUpdate();
  301. HTML_Element* new_caret_elm = GetElementOfNumber(caret_elm_nr_after);
  302. if (new_caret_elm)
  303. m_edit->m_caret.Place(new_caret_elm, caret_ofs_after);
  304. }
  305. OpDocumentEditUndoRedoEvent* merged = static_cast<OpDocumentEditUndoRedoEvent*>(First());
  306. while (merged)
  307. {
  308. OpDocumentEditUndoRedoEvent* suc_merged = static_cast<OpDocumentEditUndoRedoEvent*>(merged->Suc());
  309. merged->Redo();
  310. merged = suc_merged;
  311. }
  312. }
  313. BOOL OpDocumentEditUndoRedoEvent::IsAppending(OpDocumentEditUndoRedoEvent* previous_event)
  314. {
  315. DEBUG_CHECKER(TRUE);
  316. if (previous_event->IsInsertEvent() && IsInsertEvent())
  317. {
  318. // Adding after the previous event
  319. if (elm_nr == previous_event->elm_nr &&
  320. caret_elm_nr_before == previous_event->caret_elm_nr_after &&
  321. caret_ofs_before == previous_event->caret_ofs_after)
  322. return TRUE;
  323. }
  324. if (!previous_event->IsInsertEvent() && !IsInsertEvent())
  325. {
  326. // Deleting after previous event
  327. if (elm_nr == previous_event->elm_nr &&
  328. caret_elm_nr_after == previous_event->caret_elm_nr_after &&
  329. caret_ofs_after == previous_event->caret_ofs_after)
  330. return TRUE;
  331. // Deleting before previous event
  332. if (elm_nr == previous_event->elm_nr &&
  333. caret_elm_nr_before == previous_event->caret_elm_nr_after &&
  334. caret_ofs_before == previous_event->caret_ofs_after)
  335. return TRUE;
  336. }
  337. return FALSE;
  338. }
  339. BOOL OpDocumentEditUndoRedoEvent::IsInsertEvent()
  340. {
  341. DEBUG_CHECKER(TRUE);
  342. OP_ASSERT(type == UNDO_REDO_CARET_PLACEMENT_BEGIN || type == UNDO_REDO_CARET_PLACEMENT_END);
  343. return caret_elm_nr_before < caret_elm_nr_after ||
  344. (caret_elm_nr_before == caret_elm_nr_after && caret_ofs_before < caret_ofs_after);
  345. }
  346. void OpDocumentEditUndoRedoEvent::UpdateBytesUsed()
  347. {
  348. DEBUG_CHECKER(TRUE);
  349. // Not completly accurate. The elements may have stuff allocated (attributes and properties)
  350. bytes_used = sizeof(OpDocumentEditUndoRedoEvent);
  351. // No clone in below cases so no additional memory used for HTML_Element
  352. if ((type != UNDO_REDO_ELEMENT_INSERT || stage != UNDO) &&
  353. (type != UNDO_REDO_ELEMENT_REMOVE || stage != REDO) &&
  354. type != UNDO_REDO_CARET_PLACEMENT_BEGIN &&
  355. type != UNDO_REDO_CARET_PLACEMENT_END)
  356. {
  357. HTML_Element* tmp = element_changed;
  358. while(tmp)
  359. {
  360. if (tmp->IsIncludedActual() || tmp->GetInserted() == HE_INSERTED_BY_IME)
  361. {
  362. bytes_used += sizeof(HTML_Element);
  363. if (tmp->Type() == HE_TEXT)
  364. bytes_used += tmp->GetTextContentLength() * sizeof(uni_char);
  365. }
  366. tmp = (HTML_Element*) tmp->Next();
  367. }
  368. }
  369. }
  370. HTML_Element* OpDocumentEditUndoRedoEvent::GetElementOfNumber(INT32 nr)
  371. {
  372. DEBUG_CHECKER(TRUE);
  373. if(nr < 0)
  374. return NULL;
  375. HTML_Element* tmp = m_edit->GetRoot();
  376. for(int i = 0; i < nr && tmp;)
  377. {
  378. tmp = (HTML_Element*) tmp->Next();
  379. if (tmp && (tmp->IsIncludedActual() || tmp->GetInserted() == HE_INSERTED_BY_IME))
  380. ++i;
  381. }
  382. OP_ASSERT(tmp); // This should really not happen!
  383. return tmp;
  384. }
  385. INT32 OpDocumentEditUndoRedoEvent::GetNumberOfElement(HTML_Element* elm)
  386. {
  387. DEBUG_CHECKER(TRUE);
  388. if(!elm)
  389. return -1;
  390. INT32 nr = 0;
  391. HTML_Element* tmp = m_edit->GetRoot();
  392. while (tmp && tmp != elm)
  393. {
  394. tmp = (HTML_Element*) tmp->Next();
  395. if (tmp && (tmp->IsIncludedActual() || tmp->GetInserted() == HE_INSERTED_BY_IME))
  396. ++nr;
  397. }
  398. OP_ASSERT(tmp); // elm is not part of document tree!
  399. return nr;
  400. }
  401. HTML_Element* OpDocumentEditUndoRedoEvent::CreateClone(HTML_Element* elm, BOOL deep)
  402. {
  403. DEBUG_CHECKER(TRUE);
  404. HLDocProfile *hld_profile = m_edit->GetDoc()->GetHLDocProfile();
  405. HTML_Element* copy = m_edit->NewCopyOfElement(elm);
  406. if (!copy)
  407. return NULL;
  408. if (deep && OpStatus::IsError(copy->DeepClone(hld_profile, elm)))
  409. {
  410. m_edit->DeleteElement(copy);
  411. return NULL;
  412. }
  413. return copy;
  414. }
  415. BOOL OpDocumentEditUndoRedoEvent::UndoRedoTreeInserter::FindSucMark(HTML_Element* elm)
  416. {
  417. HTML_Element* mark_elm;
  418. OP_ASSERT(type == ELEMENT_MARK_NONE);
  419. if (elm->Suc())
  420. {
  421. mark_elm = elm->Suc();
  422. while (mark_elm && (!mark_elm->IsIncludedActual() && mark_elm->GetInserted() != HE_INSERTED_BY_IME))
  423. mark_elm = mark_elm->Suc();
  424. if (mark_elm)
  425. {
  426. mark = evt->GetNumberOfElement(mark_elm);
  427. DOCEDIT_DBG(("MARK %d, MARK_TYPE SUC, MARK_ELM_TYPE %d\n", mark, mark_elm->Type()));
  428. type = ELEMENT_MARK_SUC;
  429. return TRUE;
  430. }
  431. }
  432. return FALSE;
  433. }
  434. BOOL OpDocumentEditUndoRedoEvent::UndoRedoTreeInserter::FindPredMark(HTML_Element* elm)
  435. {
  436. HTML_Element* mark_elm;
  437. OP_ASSERT(type == ELEMENT_MARK_NONE);
  438. if (elm->Pred())
  439. {
  440. mark_elm = elm->Pred();
  441. while (mark_elm && (!mark_elm->IsIncludedActual() && mark_elm->GetInserted() != HE_INSERTED_BY_IME))
  442. mark_elm = mark_elm->Pred();
  443. if (mark_elm)
  444. {
  445. mark = evt->GetNumberOfElement(mark_elm);
  446. DOCEDIT_DBG(("MARK %d, MARK_TYPE PRED, MARK_ELM_TYPE %d\n", mark, mark_elm->Type()));
  447. type = ELEMENT_MARK_PRED;
  448. return TRUE;
  449. }
  450. }
  451. return FALSE;
  452. }
  453. BOOL OpDocumentEditUndoRedoEvent::UndoRedoTreeInserter::FindParentMark(HTML_Element* elm)
  454. {
  455. HTML_Element* mark_elm;
  456. OP_ASSERT(type == ELEMENT_MARK_NONE);
  457. if (elm->Parent())
  458. {
  459. mark_elm = elm->Parent();
  460. while (mark_elm && (!mark_elm->IsIncludedActual() && mark_elm->GetInserted() != HE_INSERTED_BY_IME))
  461. mark_elm = mark_elm->Parent();
  462. if (mark_elm)
  463. {
  464. // If the element is the last or the only one child MARK_PARENT marker is good enough for us. However if there are also some other children of mark_elm
  465. // we need to find proper position where to put this element under the parent
  466. if (mark_elm->LastChildActual() != elm)
  467. {
  468. HTML_Element* iter = mark_elm->FirstChild();
  469. while (iter)
  470. {
  471. if (iter->IsAncestorOf(elm))
  472. {
  473. if (FindSucMark(iter) || FindPredMark(iter))
  474. return TRUE;
  475. }
  476. iter = iter->Suc();
  477. }
  478. }
  479. mark = evt->GetNumberOfElement(mark_elm);
  480. DOCEDIT_DBG(("MARK %d, MARK_TYPE PARENT, MARK_ELM_TYPE %d\n", mark, mark_elm->Type()));
  481. type = ELEMENT_MARK_PARENT;
  482. return TRUE;
  483. }
  484. }
  485. return FALSE;
  486. }
  487. void OpDocumentEditUndoRedoEvent::UndoRedoTreeInserter::FindPlaceMarker(HTML_Element* elm)
  488. {
  489. OP_ASSERT(elm);
  490. type = ELEMENT_MARK_NONE;
  491. #ifdef DEBUG_ENABLE_OPASSERT
  492. BOOL found =
  493. #endif // DEBUG_ENABLE_OPASSERT
  494. FindSucMark(elm) || FindPredMark(elm) || FindParentMark(elm);
  495. OP_ASSERT(found);
  496. }
  497. void OpDocumentEditUndoRedoEvent::UndoRedoTreeInserter::Insert(HTML_Element* element, const HTML_Element::DocumentContext& context, INT32 position, BOOL mark_dirty /* = TRUE */)
  498. {
  499. OP_ASSERT(element);
  500. OP_ASSERT(type == ELEMENT_MARK_PRED || type == ELEMENT_MARK_SUC || type == ELEMENT_MARK_PARENT);
  501. OP_ASSERT(mark != -1);
  502. OP_ASSERT(evt->type == UNDO_REDO_ELEMENT_CHANGE ||
  503. (evt->type == UNDO_REDO_ELEMENT_INSERT && evt->stage == OpDocumentEditUndoRedoEvent::REDO) ||
  504. (evt->type == UNDO_REDO_ELEMENT_REMOVE && evt->stage == OpDocumentEditUndoRedoEvent::UNDO));
  505. INT32 pos = mark;
  506. BOOL retry_using_pos = FALSE;
  507. /* For below operations REMOVE occurs at some point, so when the mark is SUCCESSOR
  508. * a position must be adjusted (decreased) because it was remembered BEFORE removing
  509. * the element from the tree. The element and its children must be taken into account.
  510. */
  511. if ((evt->type == UNDO_REDO_ELEMENT_CHANGE ||
  512. (evt->type == UNDO_REDO_ELEMENT_INSERT && evt->stage == OpDocumentEditUndoRedoEvent::REDO) ||
  513. (evt->type == UNDO_REDO_ELEMENT_REMOVE && evt->stage == OpDocumentEditUndoRedoEvent::UNDO)) &&
  514. type == ELEMENT_MARK_SUC)
  515. {
  516. --pos;
  517. HTML_Element* iter = element->FirstChild();
  518. HTML_Element* stop = static_cast<HTML_Element*>(element->NextSibling());
  519. while (iter && iter != stop)
  520. {
  521. if (iter->IsIncludedActual() || iter->GetInserted() == HE_INSERTED_BY_IME)
  522. --pos;
  523. iter = iter->Next();
  524. }
  525. }
  526. do
  527. {
  528. HTML_Element* elm = evt->GetElementOfNumber(pos);
  529. if (elm)
  530. {
  531. switch (type)
  532. {
  533. case ELEMENT_MARK_PRED:
  534. {
  535. element->FollowSafe(context, elm, mark_dirty);
  536. break;
  537. }
  538. case ELEMENT_MARK_SUC:
  539. {
  540. element->PrecedeSafe(context, elm, mark_dirty);
  541. break;
  542. }
  543. case ELEMENT_MARK_PARENT:
  544. {
  545. element->UnderSafe(context, elm, mark_dirty);
  546. break;
  547. }
  548. }
  549. }
  550. if (evt->GetNumberOfElement(element) != position && !retry_using_pos)
  551. {
  552. // Something went wrong and the element is inserted in the wrong place. Try again using the position instead of the mark this time
  553. element->OutSafe(context, FALSE);
  554. pos = position;
  555. if (type == ELEMENT_MARK_PRED)
  556. --pos;
  557. }
  558. else
  559. break;
  560. retry_using_pos = !retry_using_pos;
  561. } while(retry_using_pos);
  562. }
  563. /* static */
  564. UINT32 OpDocumentEditUndoRedoEvent::AllBytesUsed(OpDocumentEditUndoRedoEvent *evt)
  565. {
  566. UINT32 count = evt->BytesUsed();
  567. OpDocumentEditUndoRedoEvent *iter = static_cast<OpDocumentEditUndoRedoEvent*>(evt->First());
  568. while (iter)
  569. {
  570. count += iter->BytesUsed();
  571. iter = static_cast<OpDocumentEditUndoRedoEvent*>(iter->Suc());
  572. }
  573. return count;
  574. }
  575. /* static */
  576. void OpDocumentEditUndoRedoEvent::UpdateAllBytesUsed(OpDocumentEditUndoRedoEvent *evt)
  577. {
  578. evt->UpdateBytesUsed();
  579. OpDocumentEditUndoRedoEvent *iter = static_cast<OpDocumentEditUndoRedoEvent*>(evt->First());
  580. while (iter)
  581. {
  582. iter->UpdateBytesUsed();
  583. iter = static_cast<OpDocumentEditUndoRedoEvent*>(iter->Suc());
  584. }
  585. }
  586. // == OpDocumentEditUndoRedoStack ==================================================
  587. OpDocumentEditUndoRedoStack::OpDocumentEditUndoRedoStack(OpDocumentEdit* edit)
  588. : m_mem_used(0)
  589. , m_edit(edit)
  590. , m_current_event(NULL)
  591. , m_begin_count(0)
  592. , m_begin_count_calls(0)
  593. , m_group_begin_count(0)
  594. , m_group_event_count(0)
  595. , m_flags(CHANGE_FLAGS_NONE)
  596. , m_disabled_count(0)
  597. {
  598. DEBUG_CHECKER_CONSTRUCTOR();
  599. m_edit->AddInternalEventListener(this);
  600. }
  601. OpDocumentEditUndoRedoStack::~OpDocumentEditUndoRedoStack()
  602. {
  603. DEBUG_CHECKER(FALSE);
  604. OP_ASSERT(m_begin_count == 0);
  605. OP_ASSERT(m_group_begin_count == 0);
  606. Clear();
  607. m_edit->RemoveInternalEventListener(this);
  608. }
  609. void OpDocumentEditUndoRedoStack::Clear(BOOL clear_undos, BOOL clear_redos)
  610. {
  611. DEBUG_CHECKER(FALSE);
  612. OpDocumentEditUndoRedoEvent* event;
  613. // Clear undos
  614. if (clear_undos)
  615. {
  616. event = static_cast<OpDocumentEditUndoRedoEvent*>(m_undos.First());
  617. while(event)
  618. {
  619. OpDocumentEditUndoRedoEvent* next_event = static_cast<OpDocumentEditUndoRedoEvent*>(event->Suc());
  620. m_mem_used -= OpDocumentEditUndoRedoEvent::AllBytesUsed(event);
  621. OP_DELETE(event);
  622. event = next_event;
  623. }
  624. }
  625. // Clear redos
  626. if (clear_redos)
  627. {
  628. event = static_cast<OpDocumentEditUndoRedoEvent*>(m_redos.First());
  629. while(event)
  630. {
  631. OpDocumentEditUndoRedoEvent* next_event = static_cast<OpDocumentEditUndoRedoEvent*>(event->Suc());
  632. m_mem_used -= OpDocumentEditUndoRedoEvent::AllBytesUsed(event);
  633. OP_DELETE(event);
  634. event = next_event;
  635. }
  636. }
  637. }
  638. OP_STATUS OpDocumentEditUndoRedoStack::BeginChange(HTML_Element* containing_elm, int flags)
  639. {
  640. if (m_disabled_count)
  641. return OpStatus::OK;
  642. DEBUG_CHECKER(FALSE);
  643. m_begin_count++;
  644. m_begin_count_calls++;
  645. if (m_begin_count > 1)
  646. {
  647. // Must be same containing element or child if nesting calls to BeginChange
  648. OP_ASSERT(containing_elm == m_current_containing_elm || m_current_containing_elm->IsAncestorOf(containing_elm));
  649. return OpStatus::OK;
  650. }
  651. OP_ASSERT(containing_elm->IsIncludedActual() || containing_elm->GetInserted() == HE_INSERTED_BY_IME);
  652. OP_ASSERT(!m_current_event);
  653. OP_ASSERT(containing_elm == m_edit->GetRoot() || m_edit->GetRoot()->IsAncestorOf(containing_elm));
  654. m_current_containing_elm = containing_elm;
  655. Clear(FALSE, TRUE);
  656. m_flags = flags;
  657. // Remember start caret placement when change(s) start.
  658. RETURN_IF_MEMORY_ERROR(BeginChange(m_current_containing_elm, NULL, UNDO_REDO_CARET_PLACEMENT_BEGIN, m_flags));
  659. RETURN_IF_MEMORY_ERROR(EndLastChange(m_current_containing_elm));
  660. return OpStatus::OK;
  661. }
  662. void OpDocumentEditUndoRedoStack::OnElementOut(HTML_Element *elm)
  663. {
  664. if (m_begin_count > 0 && (elm->IsIncludedActual() || elm->GetInserted() == HE_INSERTED_BY_IME) && m_edit->GetRoot()->IsAncestorOf(elm))
  665. {
  666. if (OpStatus::IsMemoryError(BeginChange(m_current_containing_elm, elm, UNDO_REDO_ELEMENT_REMOVE, m_flags)))
  667. m_edit->ReportOOM();
  668. if (OpStatus::IsMemoryError(EndLastChange(m_current_containing_elm)))
  669. m_edit->ReportOOM();
  670. }
  671. }
  672. void OpDocumentEditUndoRedoStack::OnElementInserted(HTML_Element* elm)
  673. {
  674. if (m_begin_count > 0 && (elm->IsIncludedActual() || elm->GetInserted() == HE_INSERTED_BY_IME) && m_edit->GetRoot()->IsAncestorOf(elm))
  675. {
  676. if (OpStatus::IsMemoryError(BeginChange(m_current_containing_elm, elm, UNDO_REDO_ELEMENT_INSERT, m_flags)))
  677. m_edit->ReportOOM();
  678. if (OpStatus::IsMemoryError(EndLastChange(m_current_containing_elm)))
  679. m_edit->ReportOOM();
  680. }
  681. }
  682. void OpDocumentEditUndoRedoStack::OnElementChange(HTML_Element *elm)
  683. {
  684. if (m_begin_count > 0 && (elm->IsIncludedActual() || elm->GetInserted() == HE_INSERTED_BY_IME) && m_edit->GetRoot()->IsAncestorOf(elm))
  685. {
  686. if (OpStatus::IsMemoryError(BeginChange(m_current_containing_elm, elm, UNDO_REDO_ELEMENT_CHANGE, m_flags)))
  687. m_edit->ReportOOM();
  688. }
  689. }
  690. void OpDocumentEditUndoRedoStack::OnElementChanged(HTML_Element *elm)
  691. {
  692. if (m_begin_count > 0 && (elm->IsIncludedActual() || elm->GetInserted() == HE_INSERTED_BY_IME) && m_edit->GetRoot()->IsAncestorOf(elm))
  693. {
  694. if (OpStatus::IsMemoryError(EndLastChange(m_current_containing_elm)))
  695. m_edit->ReportOOM();
  696. }
  697. }
  698. OP_STATUS OpDocumentEditUndoRedoStack::BeginChange(HTML_Element* containing_elm, HTML_Element* element_changed, int type, int flags)
  699. {
  700. if (m_disabled_count)
  701. return OpStatus::OK;
  702. m_current_event = OP_NEW(OpDocumentEditUndoRedoEvent, (m_edit));
  703. if (m_current_event == NULL || OpStatus::IsError(m_current_event->BeginChange(containing_elm, element_changed, type)))
  704. {
  705. AbortChange();
  706. Clear();
  707. return OpStatus::ERR_NO_MEMORY;
  708. }
  709. m_mem_used += m_current_event->BytesUsed();
  710. if (m_group_begin_count)
  711. {
  712. m_group_event_count++;
  713. }
  714. OpDocumentEditUndoRedoEvent* previous_event = static_cast<OpDocumentEditUndoRedoEvent*>(m_undos.Last());
  715. if (previous_event && previous_event->Type() == UNDO_REDO_CARET_PLACEMENT_BEGIN)
  716. {
  717. // All events between CARET_PLACEMENT_BEGIN - CARET_PLACEMENT_END should be treated as one.
  718. if (!previous_event->Empty())
  719. {
  720. if (static_cast<OpDocumentEditUndoRedoEvent*>(previous_event->Last())->Type() == UNDO_REDO_CARET_PLACEMENT_END)
  721. {
  722. if (m_current_event->Type() == UNDO_REDO_CARET_PLACEMENT_BEGIN &&
  723. flags & CHANGE_FLAGS_ALLOW_APPEND &&
  724. m_current_event->IsAppending(static_cast<OpDocumentEditUndoRedoEvent*>(previous_event->Last())) &&
  725. // Do not append user visible change to the user invisible one as it may become invisible too
  726. (!previous_event->IsUserInvisible() || flags & CHANGE_FLAGS_USER_INVISIBLE)
  727. )
  728. {
  729. if (m_current_event->MergeTo(previous_event))
  730. m_current_event = NULL;
  731. }
  732. }
  733. else
  734. {
  735. if (m_current_event->MergeTo(previous_event))
  736. m_current_event = NULL;
  737. }
  738. }
  739. else
  740. {
  741. if (m_current_event->MergeTo(previous_event))
  742. m_current_event = NULL;
  743. }
  744. }
  745. if (m_current_event)
  746. {
  747. if (flags & CHANGE_FLAGS_USER_INVISIBLE)
  748. {
  749. m_current_event->SetUserInvisible();
  750. if (m_current_event->MergeTo(previous_event))
  751. m_current_event = NULL;
  752. }
  753. else if (m_group_begin_count)
  754. {
  755. if (m_group_event_count > 1)
  756. if (m_current_event->MergeTo(static_cast<OpDocumentEditUndoRedoEvent*>(m_undos.Last())))
  757. m_current_event = NULL;
  758. }
  759. }
  760. if (m_current_event)
  761. {
  762. m_current_event->Into(&m_undos);
  763. m_current_event = NULL;
  764. }
  765. return OpStatus::OK;
  766. }
  767. OP_STATUS OpDocumentEditUndoRedoStack::EndLastChange(HTML_Element* containing_elm)
  768. {
  769. if (m_disabled_count)
  770. return OpStatus::OK;
  771. OpDocumentEditUndoRedoEvent* last = static_cast<OpDocumentEditUndoRedoEvent*>(m_undos.Last());
  772. if(!last)
  773. {
  774. OP_ASSERT(FALSE);
  775. Clear();
  776. return OpStatus::ERR;
  777. }
  778. if (!last->Empty())
  779. last = static_cast<OpDocumentEditUndoRedoEvent*>(last->Last());
  780. if (OpStatus::IsError(last->EndChange(containing_elm)))
  781. {
  782. Clear();
  783. m_current_event = NULL;
  784. return OpStatus::ERR_NO_MEMORY;
  785. }
  786. return OpStatus::OK;
  787. }
  788. OP_STATUS OpDocumentEditUndoRedoStack::EndChange(HTML_Element* containing_elm)
  789. {
  790. if (m_disabled_count)
  791. return OpStatus::OK;
  792. DEBUG_CHECKER(FALSE);
  793. OP_ASSERT(m_begin_count > 0);
  794. m_begin_count--;
  795. // Delete old events if the MAX_MEM_PER_UNDOREDOSTACK has been reached.
  796. // But always keep at least 1 eventgroup left.
  797. while (m_mem_used > MAX_MEM_PER_UNDOREDOSTACK && m_undos.Cardinal() > 1)
  798. {
  799. OpDocumentEditUndoRedoEvent* event = static_cast<OpDocumentEditUndoRedoEvent*>(m_undos.First());
  800. m_mem_used -= OpDocumentEditUndoRedoEvent::AllBytesUsed(event);
  801. OP_DELETE(event);
  802. }
  803. // Remember end caret placement when change(s) end.
  804. RETURN_IF_MEMORY_ERROR(BeginChange(m_current_containing_elm, NULL, UNDO_REDO_CARET_PLACEMENT_END, m_flags));
  805. RETURN_IF_MEMORY_ERROR(EndLastChange(m_current_containing_elm));
  806. return OpStatus::OK;
  807. }
  808. void OpDocumentEditUndoRedoStack::AbortChange()
  809. {
  810. if (m_disabled_count)
  811. return;
  812. DEBUG_CHECKER(FALSE);
  813. OP_ASSERT(m_begin_count > 0);
  814. m_begin_count--;
  815. OP_DELETE(m_current_event);
  816. m_current_event = NULL;
  817. Clear();
  818. }
  819. void OpDocumentEditUndoRedoStack::BeginGroup()
  820. {
  821. DEBUG_CHECKER(FALSE);
  822. if (m_group_begin_count == 0)
  823. m_group_event_count = 0;
  824. m_group_begin_count++;
  825. }
  826. void OpDocumentEditUndoRedoStack::EndGroup()
  827. {
  828. DEBUG_CHECKER(FALSE);
  829. m_group_begin_count--;
  830. }
  831. void OpDocumentEditUndoRedoStack::Undo()
  832. {
  833. DEBUG_CHECKER(FALSE);
  834. OpDocumentEditUndoRedoEvent* evt = static_cast<OpDocumentEditUndoRedoEvent*>(m_undos.Last());
  835. if (evt && !evt->IsUserInvisible())
  836. {
  837. OP_ASSERT(evt->Type() == UNDO_REDO_CARET_PLACEMENT_BEGIN);
  838. OP_ASSERT(evt->Last() && static_cast<OpDocumentEditUndoRedoEvent*>(evt->Last())->Type() == UNDO_REDO_CARET_PLACEMENT_END);
  839. // As depending on the event type and current stage a clone may or may not be stored - update mem used
  840. m_mem_used -= OpDocumentEditUndoRedoEvent::AllBytesUsed(evt);
  841. evt->Undo();
  842. evt->Out();
  843. evt->IntoStart(&m_redos);
  844. OpDocumentEditUndoRedoEvent::UpdateAllBytesUsed(evt);
  845. m_mem_used += OpDocumentEditUndoRedoEvent::AllBytesUsed(evt);
  846. }
  847. }
  848. void OpDocumentEditUndoRedoStack::Redo()
  849. {
  850. DEBUG_CHECKER(FALSE);
  851. OpDocumentEditUndoRedoEvent* evt = static_cast<OpDocumentEditUndoRedoEvent*>(m_redos.First());
  852. OP_ASSERT(!evt->IsUserInvisible());
  853. if (evt)
  854. {
  855. OP_ASSERT(evt->Type() == UNDO_REDO_CARET_PLACEMENT_BEGIN);
  856. OP_ASSERT(evt->Last() && static_cast<OpDocumentEditUndoRedoEvent*>(evt->Last())->Type() == UNDO_REDO_CARET_PLACEMENT_END);
  857. // As depending on the event type and current stage a clone may or may not be stored - update mem used
  858. m_mem_used -= OpDocumentEditUndoRedoEvent::AllBytesUsed(evt);
  859. evt->Redo();
  860. evt->Out();
  861. evt->Into(&m_undos);
  862. OpDocumentEditUndoRedoEvent::UpdateAllBytesUsed(evt);
  863. m_mem_used += OpDocumentEditUndoRedoEvent::AllBytesUsed(evt);
  864. }
  865. }
  866. void OpDocumentEditUndoRedoStack::MergeLastChanges()
  867. {
  868. OpDocumentEditUndoRedoEvent* last_event = static_cast<OpDocumentEditUndoRedoEvent*>(m_undos.Last());
  869. if (last_event)
  870. {
  871. OpDocumentEditUndoRedoEvent* merge_to = static_cast<OpDocumentEditUndoRedoEvent*>(last_event->Pred());
  872. if (merge_to)
  873. {
  874. last_event->Out();
  875. last_event->MergeTo(merge_to);
  876. }
  877. }
  878. }
  879. #endif // DOCUMENT_EDIT_SUPPORT