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.h 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  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. It may not be distributed
  6. * under any circumstances.
  7. */
  8. #ifndef OP_DOCUMENTEDIT_UTILS_H
  9. #define OP_DOCUMENTEDIT_UTILS_H
  10. #ifdef DOCUMENT_EDIT_SUPPORT
  11. #include "modules/logdoc/html.h"
  12. #include "modules/layout/box/box.h"
  13. #include "modules/layout/wordinfoiterator.h"
  14. #ifdef INTERNAL_SPELLCHECK_SUPPORT
  15. #include "modules/spellchecker/opspellcheckerapi.h"
  16. #endif // INTERNAL_SPELLCHECK_SUPPORT
  17. #ifdef _DOCEDIT_DEBUG
  18. class OpDocumentEditDebugCheckerObject
  19. {
  20. public:
  21. virtual ~OpDocumentEditDebugCheckerObject() {}
  22. virtual BOOL CheckBeginCount() { return TRUE; }
  23. };
  24. /**
  25. * Just a debug-construction to be used for checking that the numbers of BeginChange() and EndChange() are equal
  26. * between entrence of a function and return from the function. To use it, write DEBUG_CHECKER(TRUE); at the first line
  27. * of a function in a file that includes OpDocumentEdit.h.
  28. */
  29. class OpDocumentEditDebugChecker
  30. {
  31. public:
  32. OpDocumentEditDebugChecker(OpDocumentEdit *edit, OpDocumentEditDebugCheckerObject *obj, BOOL check_begin_count, BOOL static_context);
  33. ~OpDocumentEditDebugChecker() ;
  34. private:
  35. void UpdateCallerInfo(void *call_addr);
  36. private:
  37. BOOL m_check_begin_count;
  38. BOOL m_static_context;
  39. int m_begin_count;
  40. OpDocumentEdit *m_edit;
  41. OpDocumentEditDebugCheckerObject *m_obj;
  42. };
  43. #endif // _DOCEDIT_DEBUG
  44. Text_Box *GetTextBox(HTML_Element *helm);
  45. /**
  46. * This is a base-class for classes who's instances wishes to be forward changes in the logical tree from
  47. * OpDocumentEdit. For an object to receive those events, it must be registered in OpDocumentEdit by using
  48. * OpDocumentEdit::AddInternalEventListener, then the corresponding events will be "forwarded" to the object.
  49. */
  50. class OpDocumentEditInternalEventListener : public Link
  51. {
  52. public:
  53. virtual ~OpDocumentEditInternalEventListener() { Out(); }
  54. OpDocumentEditInternalEventListener() : Link() {}
  55. /** Stop listening to events... */
  56. virtual void StopListening() { Out(); }
  57. /** Callback for when an element has been inserted into the logical tree. */
  58. virtual void OnElementInserted(HTML_Element *helm) {}
  59. /** Callback for when an element is about to be removed from the logical tree (by HTML_Element::OutSafe). */
  60. virtual void OnElementOut(HTML_Element *helm) {}
  61. /** Callback for when an element is about to be deleted (at this point helm is probably not in the logical tree anymore). */
  62. virtual void OnElementDeleted(HTML_Element *helm) {}
  63. /** Callback for when an element is about to be changed (e.g. its attributes). */
  64. virtual void OnElementChange(HTML_Element *helm) {}
  65. /** Callback for when an element has beed changed (e.g. its attributes ). */
  66. virtual void OnElementChanged(HTML_Element *helm) {}
  67. };
  68. #ifdef INTERNAL_SPELLCHECK_SUPPORT
  69. class HTML_WordIterator : public OpSpellCheckerWordIterator, public OpDocumentEditInternalEventListener
  70. {
  71. public:
  72. HTML_WordIterator(OpDocumentEdit *edit, BOOL used_by_session, BOOL mark_separators);
  73. virtual ~HTML_WordIterator();
  74. void Reset(BOOL stop_session = TRUE);
  75. void SetRange(HTML_Element *first, HTML_Element *last);
  76. void SetAtWord(HTML_Element *helm, int ofs);
  77. BOOL Active() { return m_active; }
  78. BOOL BeenUsed() { return m_been_used; }
  79. OpDocumentEdit *GetDocumentEdit() { return m_edit; }
  80. HTML_Element *CurrentStartHelm() { return m_current_start; }
  81. HTML_Element *CurrentStopHelm() { return m_current_stop; }
  82. BOOL IsInRange(HTML_Element* elm);
  83. void RestartFromBeginning();
  84. int CurrentStartOfs() { return m_current_start_ofs; }
  85. int CurrentStopOfs() { return m_current_stop_ofs; }
  86. // <<<OpSpellCheckerWordIterator>>>
  87. virtual const uni_char *GetCurrentWord() { m_been_used = TRUE; return m_current_string.IsEmpty() ? UNI_L("") : m_current_string.CStr(); }
  88. virtual BOOL ContinueIteration();
  89. // <<<OpDocumentEditInternalEventListener>>>
  90. virtual void OnElementOut(HTML_Element *helm);
  91. virtual void OnElementInserted(HTML_Element *helm);
  92. /**
  93. * Internal docxs function that must be called when the parser or something else causes
  94. * an HE_TEXT element to be converted to an HE_TEXTGROUP element.
  95. *
  96. * @param elm The element that used to be an HE_TEXT but is now an HE_TEXTGROUP.
  97. *
  98. * @see HTML_Element::AppendText().
  99. */
  100. void OnTextConvertedToTextGroup(HTML_Element* elm);
  101. /**
  102. * Finds the start of a word.
  103. *
  104. * @return TRUE/FALSE if word was/wasn't found
  105. */
  106. BOOL FindStartOfWord();
  107. /**
  108. * Reads the word found by FindStartOfWord() and sets the position to after the word.
  109. *
  110. * @param toString If non-NULL it is set to the word. If NULL the word is just skipped and not stored.
  111. *
  112. * @return TRUE on success, FALSE otherwise.
  113. */
  114. BOOL ReadWord(OpString* toString);
  115. private:
  116. void RestartFrom(HTML_Element *helm);
  117. private:
  118. BOOL m_active;
  119. BOOL m_been_used;
  120. BOOL m_used_by_session;
  121. BOOL m_single_word;
  122. BOOL m_mark_separators;
  123. HTML_Element *m_editable_container, *m_after_editable_container;
  124. HTML_Element *m_current_start, *m_current_stop;
  125. int m_current_start_ofs, m_current_stop_ofs;
  126. HTML_Element *m_first, *m_last;
  127. OpDocumentEdit *m_edit;
  128. OpString m_current_string;
  129. };
  130. struct SpellWordInfoObject
  131. {
  132. SpellWordInfoObject() { op_memset(this, 0, sizeof(*this)); }
  133. SpellWordInfoObject(HTML_Element *helm) { Set(helm); }
  134. BOOL IsValid() { return !!helm; }
  135. void Set(HTML_Element *helm)
  136. {
  137. Text_Box *box = GetTextBox(helm);
  138. if(!box || !box->GetWordCount() || !box->GetWords())
  139. {
  140. op_memset(this, 0, sizeof(*this));
  141. return;
  142. }
  143. this->helm = helm;
  144. word_count = box->GetWordCount();
  145. last_word_misspelled = box->GetWords()[box->GetWordCount()-1].IsMisspelling();
  146. }
  147. HTML_Element *helm;
  148. int word_count;
  149. BOOL last_word_misspelled;
  150. };
  151. #endif // INTERNAL_SPELLCHECK_SUPPORT
  152. /** Simple way of adding an element in a linked list... */
  153. class OpDocumentEditAutoLink : public Link
  154. {
  155. public:
  156. virtual ~OpDocumentEditAutoLink() { Out(); }
  157. /**
  158. * @parm object The object that should be kept by this link object.
  159. * @parm head The list this link object should be inserted into.
  160. */
  161. OpDocumentEditAutoLink(void *object, Head *head) : Link(), m_object(object) { if(head) Into(head); }
  162. /** Returns the object beeing kept by this link. */
  163. void *GetObject() { return m_object; }
  164. /** Static function that returns the link from list head which keeps object, returns NULL if no such link existed. */
  165. static OpDocumentEditAutoLink *GetLink(void *object, Head *head);
  166. private:
  167. void *m_object;
  168. };
  169. /**
  170. * A class for trying to keep a range of elements (start+stop) where start and stop is under the same container which
  171. * is given to the constructor. If start is removed will start->Prev() be the new start and if stop is removed will
  172. * stop->Next() be the new stop element. If start or stop is removed from the logical tree but later inserted again
  173. * below the container will start or stop change to that element again points at the element that was removed+inserted.
  174. * If the container is deleted will the object become "invalid".
  175. */
  176. class OpDocumentEditRangeKeeper : public OpDocumentEditInternalEventListener
  177. #ifdef _DOCEDIT_DEBUG
  178. , public OpDocumentEditDebugCheckerObject
  179. #endif
  180. {
  181. public:
  182. virtual ~OpDocumentEditRangeKeeper() {}
  183. /**
  184. * See description of this class for details.
  185. * @parm container The container that should be an ancestor of start_elm and stop_elm.
  186. * @parm start_elm The start of the range that we should try keep.
  187. * @parm start_elm The end of the range that we should try keep.
  188. */
  189. OpDocumentEditRangeKeeper(HTML_Element *container, HTML_Element *start_elm, HTML_Element *stop_elm, OpDocumentEdit *edit);
  190. /** Returns TRUE if this range of some reason has become "invalid", probably due to that the container has been deleted. */
  191. BOOL IsValid() { return m_edit && m_container && m_start_elm && m_stop_elm; }
  192. /** Makes this object "invalid" explicitly and unusable. */
  193. void MakeInvalid() { m_start_elm = m_stop_elm = m_container = NULL; m_edit = NULL; }
  194. /** Return the current start element, which might have changed since the object's construction. */
  195. HTML_Element *GetStartElement() { return m_start_elm; }
  196. /** Return the current stop element, which might have changed since the object's construction. */
  197. HTML_Element *GetStopElement() { return m_stop_elm; }
  198. /** Returns the container which is either the conainer given to the constructor or NULL if the constructor
  199. has been deleted or the object has become invalid for any other reason. */
  200. HTML_Element *GetContainer() { return m_container; }
  201. // == OpDocumentEditInternalEventListener =======================
  202. virtual void OnElementInserted(HTML_Element *helm);
  203. virtual void OnElementOut(HTML_Element *helm);
  204. virtual void OnElementDeleted(HTML_Element *helm);
  205. private:
  206. HTML_Element *AdjustInDirection(HTML_Element *helm, BOOL forward, BOOL only_allow_in_direction = FALSE);
  207. private:
  208. OpDocumentEdit *m_edit;
  209. HTML_Element *m_container;
  210. HTML_Element *m_start_elm;
  211. HTML_Element *m_stop_elm;
  212. HTML_Element *m_pending_start_elm;
  213. HTML_Element *m_pending_stop_elm;
  214. };
  215. /**
  216. * This is a construction which is used for preserving whitespace's collapsing-state at the "edges" around an insertion
  217. * or deletion. As an example, in OpDocumentEditSelection::RemoveContent, this order of events occurs:
  218. *
  219. * OpDocumentEditWsPreserver preserver(start_elm, stop_elm, start_ofs, stop_ofs, m_edit);
  220. * ...delete stuff between (start_elm, start_ofs) and (stop_elm, stop_ofs)...
  221. * preserver.WsPreserve();
  222. *
  223. * If "pa" is removed from <b>hej </b>pa<b> dig</b> and the spaces are collapsable, then would the space before "dig"
  224. * be collapsed, but the WsPreserve function ensures that the space will be non-collapsed, because it was non-collapsed
  225. * before.
  226. */
  227. class OpDocumentEditWsPreserver : public Link
  228. #ifdef _DOCEDIT_DEBUG
  229. , public OpDocumentEditDebugCheckerObject
  230. #endif
  231. {
  232. public:
  233. virtual ~OpDocumentEditWsPreserver() { Out(); }
  234. OpDocumentEditWsPreserver(OpDocumentEdit *edit) : m_edit(edit) { ClearRange(); }
  235. /** Creates an instance of this class and calls SetRemoveRange, so it's just constructor + SetRemoveRange in one call, so to say. */
  236. OpDocumentEditWsPreserver(HTML_Element *start_elm, HTML_Element *stop_elm, int start_ofs, int stop_ofs, OpDocumentEdit *edit);
  237. /** Sets the preserving range to empty */
  238. void ClearRange() { m_start_elm = m_stop_elm = NULL; }
  239. /** Returns TRUE if a call to WsPreserve might have any effect. */
  240. BOOL MightWsPreserve() { return m_start_elm || m_stop_elm; }
  241. /** Returns the start-element of the whitespace preserving operation, will be NULL if the last character before the
  242. range was not a collapsable space. */
  243. HTML_Element *GetStartElement() { return m_start_elm; }
  244. /** Returns the stop-element of the whitespace preserving operation, will be NULL if the first character after the
  245. range was not a collapsable space. */
  246. HTML_Element *GetStopElement() { return m_stop_elm; }
  247. void SetStartElement(HTML_Element *start_elm) { m_start_elm = start_elm; }
  248. void SetStopElement(HTML_Element *stop_elm) { m_stop_elm = stop_elm; }
  249. /** Returns true if there was a collapsable space before the rage and that space was collapsed. */
  250. BOOL WasStartElementCollapsed() { return m_start_elm && m_start_was_collapsed; }
  251. /** Returns true there was a collapsable space after the rage and that space was collapsed. */
  252. BOOL WasStopElementCollapsed() { return m_stop_elm && m_stop_was_collapsed; }
  253. /**
  254. * Sets the range to perform the white space preserving around. FIXME: Give it a better name as it's not only used
  255. * when removing something.
  256. * @return TRUE if a collapsable space was found just before or after the range, if so we might "preserve" it/them later
  257. * using WsPreserve.
  258. * @start_elm The element where the e.g. remove-operation should start, the scan for the collapsable whitespace will start
  259. * BEFORE (start_elm, start_ofs).
  260. * @stop_elm The element where the e.g. remove-operation should stop, the scan for the collapsable whitespace will start
  261. * AT (stop_elm, stop_ofs).
  262. * @start_ofs The offset into start_elm where the e.g. remove-operation should start, the scan for the collapsable whitespace will start
  263. * BEFORE (start_elm, start_ofs).
  264. * @stop_ofs The offset into stop_elm where the e.g. remove-operation should stop, the scan for the collapsable whitespace will start
  265. * AT (stop_elm, stop_ofs).
  266. */
  267. BOOL SetRemoveRange(HTML_Element *start_elm, HTML_Element *stop_elm, int start_ofs, int stop_ofs, BOOL check_start = TRUE);
  268. /** If there where any collapsable whitespaces to "preserve", this function performs the preserving. */
  269. BOOL WsPreserve();
  270. private:
  271. BOOL GetCollapsedCountFrom(HTML_Element *helm, int ofs, int &found_count);
  272. HTML_Element *GetNextFriendlyTextHelm(HTML_Element *helm);
  273. BOOL DeleteCollapsedFrom(HTML_Element *helm, int ofs, BOOL insert_nbsp_first);
  274. private:
  275. OpDocumentEdit *m_edit;
  276. HTML_Element *m_start_elm, *m_stop_elm;
  277. int m_start_ofs, m_stop_ofs;
  278. BOOL m_start_was_collapsed, m_stop_was_collapsed;
  279. };
  280. /**
  281. * Class that receives callbacks on deleting html elements in order to
  282. * reset the pointers in the OpDocumentEditWsPreserver class.
  283. */
  284. class OpDocumentEditWsPreserverContainer
  285. : public OpDocumentEditInternalEventListener
  286. {
  287. public:
  288. OpDocumentEditWsPreserverContainer(OpDocumentEditWsPreserver *preserver, OpDocumentEdit *edit);
  289. //virtual void OnElementInserted(HTML_Element *helm);
  290. //virtual void OnElementOut(HTML_Element *helm);
  291. virtual void OnElementDeleted(HTML_Element *helm);
  292. private:
  293. OpDocumentEditWsPreserver *m_preserver;
  294. OpDocumentEdit *m_edit;
  295. };
  296. /**
  297. * OpDocumentEditWordIterator is an abstraction for easier access to the layout word styling information.
  298. * It also contains some logic for determining valid caret-positions inside text-elements.
  299. */
  300. class DocEditWordIteratorSurroundChecker : public WordInfoIterator::SurroundingsInformation
  301. {
  302. private:
  303. OpDocumentEdit* m_edit;
  304. public:
  305. DocEditWordIteratorSurroundChecker(OpDocumentEdit *edit) : m_edit(edit) {}
  306. virtual BOOL HasWsPreservingElmBeside(HTML_Element* helm, BOOL before);
  307. };
  308. class OpDocumentEditWordIterator : public WordInfoIterator
  309. #ifdef _DOCEDIT_DEBUG
  310. , public OpDocumentEditDebugCheckerObject
  311. #endif
  312. {
  313. public:
  314. virtual ~OpDocumentEditWordIterator();
  315. /** Constructor, helm should be a HE_TEXT element and edit an instance of OpDocumentEdit as some functions like
  316. OpDocumentEdit::ReflowAndUpdate and OpDocumentEdit::IsFriends are used. */
  317. OpDocumentEditWordIterator(HTML_Element* helm, OpDocumentEdit *edit);
  318. /** Returns error status code from the constructor, should be checked before using any functions below. */
  319. OP_STATUS GetStatus() { return m_status; }
  320. /** Returns TRUE if there are no valid caret-position in the element. */
  321. BOOL IsCollapsed() { return !IsValidForCaret(); }
  322. /** The same as !IsCollapsed(), this means - it returns TRUE if there is a valid caret-offset in the element. */
  323. BOOL IsValidForCaret(BOOL valid_if_possible = FALSE);
  324. /** Retrieves the first valid caret-offset into the element in res_ofs, returns FALSE if the element has no valid caret-position. */
  325. BOOL GetFirstValidCaretOfs(int &res_ofs);
  326. /** Retrieves the last valid caret-offset into the element in res_ofs, returns FALSE if the element has no valid caret-position. */
  327. BOOL GetLastValidCaretOfs(int &res_ofs);
  328. /**
  329. * "Snaps" offset ofs to a valid caret-ofset.
  330. * @return FALSE if the element has no valid caret-offset.
  331. * @parm ofs The offset to start the scan from.
  332. * @parm res_ofs The result, if ofs was valid will res_ofs be equals to ofs. If ofs is after the the last valid
  333. * caret-offset will we snap to the last valid caret-offset, else we'll snap not the next valid caret-offset.
  334. */
  335. BOOL SnapToValidCaretOfs(int ofs, int &res_ofs);
  336. /** Same as SnapToValidCaretOfs but res_ofs will be the offset as it would have been if all collapsed characters
  337. in the element where removed, ofs should still be an offset into the full text-content of the element though.
  338. See description of SnapToValidCaretOfs. */
  339. BOOL GetCaretOfsWithoutCollapsed(int ofs, int &res_ofs);
  340. /**
  341. * Retrieves the next of previous valid character-offset starting from caret-offset ofs.
  342. * @return TRUE if such offset was found.
  343. * @parm ofs The offset to start from, ofs might be valid or not
  344. * @parm res_ofs The result...
  345. * @parm TRUE if we should scan forward, else backward.
  346. */
  347. BOOL GetValidCaretOfsFrom(int ofs, int &res_ofs, BOOL forward);
  348. /** Wrapper around GetValidCaretOfsFrom with forward == TRUE, see GetValidCaretOfsFrom. */
  349. BOOL GetNextValidCaretOfs(int ofs, int &res_ofs) { return GetValidCaretOfsFrom(ofs,res_ofs,TRUE); }
  350. /** Wrapper around GetValidCaretOfsFrom with forward == FALSE, see GetValidCaretOfsFrom. */
  351. BOOL GetPrevValidCaretOfs(int ofs, int &res_ofs) { return GetValidCaretOfsFrom(ofs,res_ofs,FALSE); }
  352. private:
  353. BOOL HasWsPreservingElmBeside(BOOL before);
  354. DocEditWordIteratorSurroundChecker m_surround_checker;
  355. OP_STATUS m_status;
  356. OpDocumentEdit *m_edit;
  357. BOOL3 m_is_valid_for_caret;
  358. };
  359. /** Stores position of a non-actual element.
  360. *
  361. * Non-actual elements, notably inserted-by-layout, are recreated during
  362. * reflow therefore a regular pointer to such an element will become invalid
  363. * after a reflow.
  364. *
  365. * This class stores the position of an element relative to its nearest
  366. * next or previous actual element. Then an element at that relative position
  367. * may be retrieved even if it has been regenerated by layout.
  368. * The correct element will only be returned if there have been no changes to
  369. * that part of the tree.
  370. */
  371. class NonActualElementPosition
  372. {
  373. public:
  374. NonActualElementPosition() : m_actual_reference(NULL), m_offset(0), m_forward(FALSE) {}
  375. /** Initializes the object.
  376. *
  377. * @param non_actual_element The element whose position will be stored. May be NULL.
  378. * @param search_reference_forward Whether to use the next or the previous
  379. * actual element as a reference.
  380. *
  381. * @return
  382. * - OK if initialized successfully,
  383. * - ERR if no reference element can be found.
  384. */
  385. OP_STATUS Construct(HTML_Element* non_actual_element, BOOL search_reference_forward = FALSE);
  386. HTML_Element* Get() const;
  387. private:
  388. HTML_Element* m_actual_reference;
  389. unsigned int m_offset;
  390. BOOL m_forward;
  391. };
  392. #endif // DOCUMENT_EDIT_SUPPORT
  393. #endif // OP_DOCUMENTEDIT_UTILS_H