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.

OpDocumentEditIME.cpp 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  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. #include "core/pch.h"
  9. #include "modules/documentedit/OpDocumentEdit.h"
  10. #include "modules/display/coreview/coreview.h"
  11. #include "modules/logdoc/src/textdata.h"
  12. #ifdef DOCUMENT_EDIT_SUPPORT
  13. #ifdef WIDGETS_IME_SUPPORT
  14. #ifndef DOCUMENTEDIT_DISABLE_IME_SUPPORT
  15. IM_WIDGETINFO OpDocumentEdit::GetIMInfo()
  16. {
  17. VisualDevice* vd = m_doc->GetVisualDevice();
  18. IM_WIDGETINFO info;
  19. info.font = NULL;
  20. info.rect = OpRect(m_doc->GetCaretPainter()->GetX() - vd->GetRenderingViewX(), m_doc->GetCaretPainter()->GetY() - vd->GetRenderingViewY(), 0, m_doc->GetCaretPainter()->GetHeight());
  21. // Scale
  22. info.rect = vd->ScaleToScreen(info.rect);
  23. // Make relative to screen
  24. OpPoint screenpos(0, 0);
  25. screenpos = vd->GetView()->ConvertToScreen(screenpos);
  26. info.rect.OffsetBy(screenpos);
  27. info.is_multiline = FALSE;
  28. return info;
  29. }
  30. HTML_Element* OpDocumentEdit::BuildInputMethodStringElement()
  31. {
  32. // Create a span with red bottomborder
  33. HTML_Element* ime_string_elm = NewElement(HE_SPAN);
  34. HTML_Element* text_elm = NewTextElement(m_imstring->Get(), uni_strlen(m_imstring->Get()));
  35. if (!ime_string_elm || !text_elm)
  36. {
  37. DeleteElement(ime_string_elm);
  38. DeleteElement(text_elm);
  39. return NULL;
  40. }
  41. ime_string_elm->SetInserted(HE_INSERTED_BY_IME);
  42. ime_string_elm->SetIMEStyling(1); // Will get the underlining property
  43. text_elm->UnderSafe(m_doc, ime_string_elm);
  44. if (m_imstring->GetCandidateLength())
  45. {
  46. SplitElement(text_elm, m_imstring->GetCandidatePos() + m_imstring->GetCandidateLength());
  47. BOOL split = SplitElement(text_elm, m_imstring->GetCandidatePos());
  48. HTML_Element* candidate_span = NewElement(HE_SPAN);
  49. if (!candidate_span)
  50. {
  51. DeleteElement(ime_string_elm);
  52. DeleteElement(text_elm);
  53. return NULL;
  54. }
  55. candidate_span->SetInserted(HE_INSERTED_BY_IME);
  56. candidate_span->SetIMEStyling(2); // Will get the candidate styling
  57. HTML_Element* candidate_element = ime_string_elm->FirstChild();
  58. if (split)
  59. candidate_element = candidate_element->Suc();
  60. candidate_span->PrecedeSafe(m_doc, candidate_element);
  61. candidate_element->OutSafe(m_doc, FALSE);
  62. candidate_element->UnderSafe(m_doc, candidate_span);
  63. }
  64. return ime_string_elm;
  65. }
  66. static HTML_Element* GetImeStringTextElement(HTML_Element *root, HTML_Element **ime_elm)
  67. {
  68. HTML_Element *elm = root;
  69. HTML_Element *stop = root->NextSibling();
  70. while (elm != stop && elm->GetInserted() != HE_INSERTED_BY_IME)
  71. elm = elm->Next();
  72. if (elm && elm->GetInserted() == HE_INSERTED_BY_IME)
  73. {
  74. if (ime_elm)
  75. *ime_elm = elm;
  76. elm = elm->FirstChildActual();
  77. if (elm && elm->Type() == HE_TEXT)
  78. return elm;
  79. }
  80. return NULL;
  81. }
  82. void
  83. OpDocumentEdit::EmptyInputMethodStringElement(BOOL remove)
  84. {
  85. if (m_ime_string_elm)
  86. {
  87. OP_ASSERT(m_caret.GetElement());
  88. HTML_Element *ime_elm;
  89. HTML_Element *elm = GetImeStringTextElement(m_ime_string_elm, &ime_elm);
  90. if (elm)
  91. {
  92. elm->SetText(m_doc, UNI_L(""), 0, HTML_Element::MODIFICATION_DELETING);
  93. if (elm == m_caret.GetElement())
  94. m_caret.Place(elm, 0);
  95. if (remove)
  96. {
  97. elm->Out();
  98. if (ime_elm->Suc())
  99. elm->Precede(ime_elm->Suc());
  100. else
  101. elm->Under(ime_elm->Parent());
  102. ime_elm->Remove(m_doc);
  103. if (ime_elm->Clean())
  104. ime_elm->Free(m_doc);
  105. }
  106. }
  107. ReflowAndUpdate();
  108. OP_ASSERT(m_caret.GetElement() || m_caret.m_parent_candidate);
  109. if (!m_caret.GetElement())
  110. m_caret.Init(TRUE);
  111. }
  112. }
  113. IM_WIDGETINFO OpDocumentEdit::OnStartComposing(OpInputMethodString* imstring, IM_COMPOSE compose)
  114. {
  115. OP_ASSERT(!m_ime_string_elm);
  116. if (m_readonly)
  117. return GetIMInfo();
  118. im_waiting_first_compose = TRUE;
  119. m_imstring = imstring;
  120. return GetIMInfo();
  121. }
  122. IM_WIDGETINFO OpDocumentEdit::OnCompose()
  123. {
  124. OP_ASSERT(m_imstring);
  125. if (m_imstring == NULL)
  126. return GetIMInfo();
  127. CheckLogTreeChanged();
  128. BOOL already_inserted = !!m_ime_string_elm;
  129. EmptyInputMethodStringElement();
  130. if (already_inserted)
  131. {
  132. HTML_Element *elm = GetImeStringTextElement(m_ime_string_elm, NULL);
  133. if (elm)
  134. elm->SetText(m_doc, m_imstring->Get(), uni_strlen(m_imstring->Get()));
  135. }
  136. else
  137. m_ime_string_elm = BuildInputMethodStringElement();
  138. if (!m_ime_string_elm)
  139. {
  140. ReportOOM();
  141. m_imstring = NULL;
  142. return GetIMInfo();
  143. }
  144. // Do not include this deletion action in the autogroup,or it will be undo in a following OnCompose message
  145. if(im_waiting_first_compose)
  146. {
  147. if (m_selection.HasContent())
  148. m_selection.RemoveContent();
  149. im_waiting_first_compose = FALSE;
  150. }
  151. OpDocumentEditDisableUndoRegistrationAuto disable(&m_undo_stack);
  152. // Since we are using up the pending styles in InsertElement, we must clone them and add them again to make
  153. // the next OnCompose use the styles again.
  154. Head pending_styles;
  155. if (OpStatus::IsMemoryError(ClonePendingStyles(pending_styles)))
  156. ReportOOM();
  157. m_caret.LockUpdatePos(TRUE);
  158. // Mark clean. Workaround for the fact that dirty elements that is inserted doesn't (and won't) mark parents dirty.
  159. m_ime_string_elm->MarkClean();
  160. HTML_Element* outmost_style;
  161. InsertPendingStyles(&outmost_style);
  162. if (!already_inserted)
  163. {
  164. InsertElement(m_ime_string_elm);
  165. if (m_ime_string_elm->Parent() == NULL)
  166. {
  167. m_ime_string_elm->Remove(m_doc);
  168. if (m_ime_string_elm->Clean())
  169. m_ime_string_elm->Free(m_doc);
  170. m_ime_string_elm = NULL;
  171. }
  172. }
  173. m_caret.Place(FindElementAfterOfType(m_ime_string_elm, HE_TEXT, FALSE), m_imstring->GetCaretPos());
  174. if (outmost_style)
  175. m_ime_string_elm = outmost_style;
  176. m_caret.LockUpdatePos(FALSE);
  177. ScrollIfNeeded();
  178. AddPendingStyles(pending_styles);
  179. return GetIMInfo();
  180. }
  181. IM_WIDGETINFO OpDocumentEdit::GetWidgetInfo()
  182. {
  183. return GetIMInfo();
  184. }
  185. void OpDocumentEdit::OnCommitResult()
  186. {
  187. if (m_imstring == NULL)
  188. return;
  189. CheckLogTreeChanged();
  190. EmptyInputMethodStringElement(TRUE);
  191. OP_STATUS status = InsertText(m_imstring->Get(), uni_strlen(m_imstring->Get()));
  192. if (OpStatus::IsMemoryError(status))
  193. ReportOOM();
  194. m_ime_string_elm = NULL;
  195. im_waiting_first_compose = FALSE;
  196. }
  197. void OpDocumentEdit::OnStopComposing(BOOL canceled)
  198. {
  199. OP_ASSERT(m_imstring);
  200. if (m_imstring == NULL)
  201. return;
  202. CheckLogTreeChanged();
  203. if (IsImComposing())
  204. {
  205. if(canceled)
  206. EmptyInputMethodStringElement(TRUE);
  207. else
  208. OnCommitResult();
  209. }
  210. m_ime_string_elm = NULL;
  211. m_undo_stack.Clear(FALSE, TRUE);
  212. im_waiting_first_compose = FALSE;
  213. m_imstring = NULL;
  214. }
  215. #endif // DOCUMENTEDIT_DISABLE_IME_SUPPORT
  216. #ifdef IME_RECONVERT_SUPPORT
  217. void OpDocumentEdit::OnPrepareReconvert(const uni_char*& str, int& sel_start, int& sel_stop)
  218. {
  219. // get selection
  220. SelectionBoundaryPoint anchor,focus;
  221. this->GetSelection(anchor,focus);
  222. HTML_Element *start = anchor.GetElement()
  223. ,*stop = focus.GetElement();
  224. int start_ofs = anchor.GetElementCharacterOffset(),
  225. stop_ofs = focus.GetElementCharacterOffset();
  226. // make sure start precedes stop
  227. if ( stop->Precedes(start) )
  228. {
  229. HTML_Element* tmp = start;
  230. start = stop;
  231. stop = tmp;
  232. int itmp = start_ofs;
  233. start_ofs = stop_ofs;
  234. stop_ofs = itmp;
  235. }
  236. // move to first text element
  237. HTML_Element* iter = start;
  238. while (!iter->IsText() && iter != stop)
  239. {
  240. iter = iter->Suc();
  241. }
  242. // set str to the whole element.
  243. if ( iter->IsText() )
  244. {
  245. str = iter->GetTextData()->GetText();
  246. if ( iter == start )
  247. sel_start = start_ofs;
  248. else
  249. sel_start = 0;
  250. if (iter == stop)
  251. sel_stop = stop_ofs;
  252. else
  253. sel_stop = iter->GetTextData()->GetTextLen();
  254. }
  255. }
  256. void OpDocumentEdit::OnReconvertRange(int sel_start, int sel_stop)
  257. {
  258. // get selection
  259. SelectionBoundaryPoint anchor,focus;
  260. this->GetSelection(anchor,focus);
  261. HTML_Element *start = anchor.GetElement()
  262. ,*stop = focus.GetElement();
  263. int start_ofs = anchor.GetElementCharacterOffset(),
  264. stop_ofs = focus.GetElementCharacterOffset();
  265. // make sure start precedes stop
  266. if ( stop->Precedes(start) )
  267. {
  268. HTML_Element* tmp = start;
  269. start = stop;
  270. stop = tmp;
  271. int itmp = start_ofs;
  272. start_ofs = stop_ofs;
  273. stop_ofs = itmp;
  274. }
  275. // move to first text element
  276. HTML_Element* iter = start;
  277. while (!iter->IsText() && iter != stop)
  278. {
  279. iter = iter->Suc();
  280. }
  281. OP_ASSERT(iter->IsText());
  282. // set selection and caret
  283. OldStyleTextSelectionPoint txt_sel_start,txt_sel_stop;
  284. txt_sel_start.SetLogicalPosition(iter,sel_start);
  285. txt_sel_stop.SetLogicalPosition(iter,sel_stop);
  286. m_selection.Select(&txt_sel_stop,&txt_sel_start);
  287. }
  288. #endif // IME_RECONVERT_SUPPORT
  289. #endif // WIDGETS_IME_SUPPORT
  290. #endif // DOCUMENT_EDIT_SUPPORT