Handle Emoji clustering for cursor handling
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-text-updater.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/text-controller-text-updater.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <memory.h>
24
25 // INTERNAL INCLUDES
26 #include <dali-toolkit/internal/text/character-set-conversion.h>
27 #include <dali-toolkit/internal/text/characters-helper-functions.h>
28 #include <dali-toolkit/internal/text/emoji-helper.h>
29 #include <dali-toolkit/internal/text/markup-processor.h>
30 #include <dali-toolkit/internal/text/text-controller-impl.h>
31 #include <dali-toolkit/internal/text/text-controller-placeholder-handler.h>
32 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
33
34 namespace
35 {
36 #if defined(DEBUG_ENABLED)
37 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
38 #endif
39
40 } // namespace
41
42 namespace Dali
43 {
44 namespace Toolkit
45 {
46 namespace Text
47 {
48 void Controller::TextUpdater::SetText(Controller& controller, const std::string& text)
49 {
50   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::SetText\n");
51
52   Controller::Impl& impl = *controller.mImpl;
53
54   // Reset keyboard as text changed
55   impl.ResetInputMethodContext();
56
57   // Remove the previously set text and style.
58   ResetText(controller);
59
60   // Remove the style.
61   impl.ClearStyleData();
62
63   CharacterIndex lastCursorIndex = 0u;
64
65   EventData*& eventData = impl.mEventData;
66
67   if(nullptr != eventData)
68   {
69     // If popup shown then hide it by switching to Editing state
70     if((EventData::SELECTING == eventData->mState) ||
71        (EventData::EDITING_WITH_POPUP == eventData->mState) ||
72        (EventData::EDITING_WITH_GRAB_HANDLE == eventData->mState) ||
73        (EventData::EDITING_WITH_PASTE_POPUP == eventData->mState))
74     {
75       if((impl.mSelectableControlInterface != nullptr) && (EventData::SELECTING == eventData->mState))
76       {
77         impl.mSelectableControlInterface->SelectionChanged(eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition, eventData->mPrimaryCursorPosition, eventData->mPrimaryCursorPosition);
78       }
79
80       impl.ChangeState(EventData::EDITING);
81     }
82   }
83
84   if(!text.empty())
85   {
86     ModelPtr&        model        = impl.mModel;
87     LogicalModelPtr& logicalModel = model->mLogicalModel;
88     model->mVisualModel->SetTextColor(impl.mTextColor);
89
90     MarkupProcessData markupProcessData(logicalModel->mColorRuns,
91                                         logicalModel->mFontDescriptionRuns,
92                                         logicalModel->mEmbeddedItems,
93                                         logicalModel->mAnchors,
94                                         logicalModel->mUnderlinedCharacterRuns,
95                                         logicalModel->mBackgroundColorRuns,
96                                         logicalModel->mStrikethroughCharacterRuns,
97                                         logicalModel->mBoundedParagraphRuns);
98
99     Length         textSize = 0u;
100     const uint8_t* utf8     = NULL;
101     if(impl.mMarkupProcessorEnabled)
102     {
103       ProcessMarkupString(text, markupProcessData);
104       textSize = markupProcessData.markupProcessedText.size();
105
106       // This is a bit horrible but std::string returns a (signed) char*
107       utf8 = reinterpret_cast<const uint8_t*>(markupProcessData.markupProcessedText.c_str());
108     }
109     else
110     {
111       textSize = text.size();
112
113       // This is a bit horrible but std::string returns a (signed) char*
114       utf8 = reinterpret_cast<const uint8_t*>(text.c_str());
115     }
116
117     //  Convert text into UTF-32
118     Vector<Character>& utf32Characters = logicalModel->mText;
119     utf32Characters.Resize(textSize);
120
121     // Transform a text array encoded in utf8 into an array encoded in utf32.
122     // It returns the actual number of characters.
123     Length characterCount = Utf8ToUtf32(utf8, textSize, utf32Characters.Begin());
124     utf32Characters.Resize(characterCount);
125
126     DALI_ASSERT_DEBUG(textSize >= characterCount && "Invalid UTF32 conversion length");
127     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::SetText %p UTF8 size %d, UTF32 size %d\n", &controller, textSize, logicalModel->mText.Count());
128
129     // The characters to be added.
130     impl.mTextUpdateInfo.mNumberOfCharactersToAdd = logicalModel->mText.Count();
131
132     // To reset the cursor position
133     lastCursorIndex = characterCount;
134
135     // Update the rest of the model during size negotiation
136     impl.QueueModifyEvent(ModifyEvent::TEXT_REPLACED);
137
138     // The natural size needs to be re-calculated.
139     impl.mRecalculateNaturalSize = true;
140
141     // The text direction needs to be updated.
142     impl.mUpdateTextDirection = true;
143
144     // Apply modifications to the model
145     impl.mOperationsPending = ALL_OPERATIONS;
146   }
147   else
148   {
149     PlaceholderHandler::ShowPlaceholderText(impl);
150   }
151
152   unsigned int oldCursorPos = (nullptr != eventData ? eventData->mPrimaryCursorPosition : 0);
153
154   // Resets the cursor position.
155   controller.ResetCursorPosition(lastCursorIndex);
156
157   // Scrolls the text to make the cursor visible.
158   impl.ResetScrollPosition();
159
160   impl.RequestRelayout();
161
162   if(nullptr != eventData)
163   {
164     // Cancel previously queued events
165     eventData->mEventQueue.clear();
166   }
167
168   // Do this last since it provides callbacks into application code.
169   if(NULL != impl.mEditableControlInterface)
170   {
171     impl.mEditableControlInterface->CursorPositionChanged(oldCursorPos, lastCursorIndex);
172     impl.mEditableControlInterface->TextChanged(true);
173   }
174 }
175
176 void Controller::TextUpdater::InsertText(Controller& controller, const std::string& text, Controller::InsertType type)
177 {
178   Controller::Impl& impl      = *controller.mImpl;
179   EventData*&       eventData = impl.mEventData;
180
181   DALI_ASSERT_DEBUG(nullptr != eventData && "Unexpected InsertText")
182
183   if(NULL == eventData)
184   {
185     return;
186   }
187
188   bool         removedPrevious  = false;
189   bool         removedSelected  = false;
190   bool         maxLengthReached = false;
191   unsigned int oldCursorPos     = eventData->mPrimaryCursorPosition;
192
193   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n", &controller, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"), eventData->mPrimaryCursorPosition, eventData->mPreEditFlag, eventData->mPreEditStartPosition, eventData->mPreEditLength);
194
195   ModelPtr&        model        = impl.mModel;
196   LogicalModelPtr& logicalModel = model->mLogicalModel;
197
198   // TODO: At the moment the underline runs are only for pre-edit.
199   model->mVisualModel->mUnderlineRuns.Clear();
200
201   // Remove the previous InputMethodContext pre-edit.
202   if(eventData->mPreEditFlag && (0u != eventData->mPreEditLength))
203   {
204     removedPrevious = RemoveText(controller,
205                                  -static_cast<int>(eventData->mPrimaryCursorPosition - eventData->mPreEditStartPosition),
206                                  eventData->mPreEditLength,
207                                  DONT_UPDATE_INPUT_STYLE);
208
209     eventData->mPrimaryCursorPosition = eventData->mPreEditStartPosition;
210     eventData->mPreEditLength         = 0u;
211   }
212   else
213   {
214     // Remove the previous Selection.
215     removedSelected = RemoveSelectedText(controller);
216   }
217
218   Vector<Character> utf32Characters;
219   Length            characterCount = 0u;
220
221   if(!text.empty())
222   {
223     //  Convert text into UTF-32
224     utf32Characters.Resize(text.size());
225
226     // This is a bit horrible but std::string returns a (signed) char*
227     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>(text.c_str());
228
229     // Transform a text array encoded in utf8 into an array encoded in utf32.
230     // It returns the actual number of characters.
231     characterCount = Utf8ToUtf32(utf8, text.size(), utf32Characters.Begin());
232     utf32Characters.Resize(characterCount);
233
234     DALI_ASSERT_DEBUG(text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length");
235     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count());
236   }
237
238   if(0u != utf32Characters.Count()) // Check if Utf8ToUtf32 conversion succeeded
239   {
240     // The placeholder text is no longer needed
241     if(impl.IsShowingPlaceholderText())
242     {
243       ResetText(controller);
244     }
245
246     impl.ChangeState(EventData::EDITING);
247
248     // Handle the InputMethodContext (predicitive text) state changes
249     if(COMMIT == type)
250     {
251       // InputMethodContext is no longer handling key-events
252       impl.ClearPreEditFlag();
253     }
254     else // PRE_EDIT
255     {
256       if(!eventData->mPreEditFlag)
257       {
258         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Entered PreEdit state\n");
259
260         // Record the start of the pre-edit text
261         eventData->mPreEditStartPosition = eventData->mPrimaryCursorPosition;
262       }
263
264       eventData->mPreEditLength = utf32Characters.Count();
265       eventData->mPreEditFlag   = true;
266
267       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", eventData->mPreEditStartPosition, eventData->mPreEditLength);
268     }
269
270     const Length numberOfCharactersInModel = logicalModel->mText.Count();
271
272     // Restrict new text to fit within Maximum characters setting.
273     Length temp_length      = (impl.mMaximumNumberOfCharacters > numberOfCharactersInModel ? impl.mMaximumNumberOfCharacters - numberOfCharactersInModel : 0);
274     Length maxSizeOfNewText = std::min(temp_length, characterCount);
275     maxLengthReached        = (characterCount > maxSizeOfNewText);
276
277     // The cursor position.
278     CharacterIndex& cursorIndex = eventData->mPrimaryCursorPosition;
279
280     // Update the text's style.
281
282     // Updates the text style runs by adding characters.
283     logicalModel->UpdateTextStyleRuns(cursorIndex, maxSizeOfNewText);
284
285     // Get the character index from the cursor index.
286     const CharacterIndex styleIndex = (cursorIndex > 0u) ? cursorIndex - 1u : 0u;
287
288     // Retrieve the text's style for the given index.
289     InputStyle style;
290     impl.RetrieveDefaultInputStyle(style);
291     logicalModel->RetrieveStyle(styleIndex, style);
292
293     InputStyle& inputStyle = eventData->mInputStyle;
294
295     // Whether to add a new text color run.
296     const bool addColorRun = (style.textColor != inputStyle.textColor) && !inputStyle.isDefaultColor;
297
298     // Whether to add a new font run.
299     const bool addFontNameRun   = (style.familyName != inputStyle.familyName) && inputStyle.isFamilyDefined;
300     const bool addFontWeightRun = (style.weight != inputStyle.weight) && inputStyle.isWeightDefined;
301     const bool addFontWidthRun  = (style.width != inputStyle.width) && inputStyle.isWidthDefined;
302     const bool addFontSlantRun  = (style.slant != inputStyle.slant) && inputStyle.isSlantDefined;
303     const bool addFontSizeRun   = (style.size != inputStyle.size) && inputStyle.isSizeDefined;
304
305     // Add style runs.
306     if(addColorRun)
307     {
308       const VectorBase::SizeType numberOfRuns = logicalModel->mColorRuns.Count();
309       logicalModel->mColorRuns.Resize(numberOfRuns + 1u);
310
311       ColorRun& colorRun                       = *(logicalModel->mColorRuns.Begin() + numberOfRuns);
312       colorRun.color                           = inputStyle.textColor;
313       colorRun.characterRun.characterIndex     = cursorIndex;
314       colorRun.characterRun.numberOfCharacters = maxSizeOfNewText;
315     }
316
317     if(addFontNameRun ||
318        addFontWeightRun ||
319        addFontWidthRun ||
320        addFontSlantRun ||
321        addFontSizeRun)
322     {
323       const VectorBase::SizeType numberOfRuns = logicalModel->mFontDescriptionRuns.Count();
324       logicalModel->mFontDescriptionRuns.Resize(numberOfRuns + 1u);
325
326       FontDescriptionRun& fontDescriptionRun = *(logicalModel->mFontDescriptionRuns.Begin() + numberOfRuns);
327
328       if(addFontNameRun)
329       {
330         fontDescriptionRun.familyLength = inputStyle.familyName.size();
331         fontDescriptionRun.familyName   = new char[fontDescriptionRun.familyLength];
332         memcpy(fontDescriptionRun.familyName, inputStyle.familyName.c_str(), fontDescriptionRun.familyLength);
333         fontDescriptionRun.familyDefined = true;
334
335         // The memory allocated for the font family name is freed when the font description is removed from the logical model.
336       }
337
338       if(addFontWeightRun)
339       {
340         fontDescriptionRun.weight        = inputStyle.weight;
341         fontDescriptionRun.weightDefined = true;
342       }
343
344       if(addFontWidthRun)
345       {
346         fontDescriptionRun.width        = inputStyle.width;
347         fontDescriptionRun.widthDefined = true;
348       }
349
350       if(addFontSlantRun)
351       {
352         fontDescriptionRun.slant        = inputStyle.slant;
353         fontDescriptionRun.slantDefined = true;
354       }
355
356       if(addFontSizeRun)
357       {
358         fontDescriptionRun.size        = static_cast<PointSize26Dot6>(inputStyle.size * impl.GetFontSizeScale() * 64.f);
359         fontDescriptionRun.sizeDefined = true;
360       }
361
362       fontDescriptionRun.characterRun.characterIndex     = cursorIndex;
363       fontDescriptionRun.characterRun.numberOfCharacters = maxSizeOfNewText;
364     }
365
366     // Insert at current cursor position.
367     Vector<Character>& modifyText = logicalModel->mText;
368
369     auto pos = modifyText.End();
370     if(cursorIndex < numberOfCharactersInModel)
371     {
372       pos = modifyText.Begin() + cursorIndex;
373     }
374     unsigned int realPos = pos - modifyText.Begin();
375     modifyText.Insert(pos, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText);
376
377     if(NULL != impl.mEditableControlInterface)
378     {
379       impl.mEditableControlInterface->TextInserted(realPos, maxSizeOfNewText, text);
380     }
381
382     TextUpdateInfo& textUpdateInfo = impl.mTextUpdateInfo;
383
384     // Mark the first paragraph to be updated.
385     if(Layout::Engine::SINGLE_LINE_BOX == impl.mLayoutEngine.GetLayout())
386     {
387       textUpdateInfo.mCharacterIndex             = 0;
388       textUpdateInfo.mNumberOfCharactersToRemove = textUpdateInfo.mPreviousNumberOfCharacters;
389       textUpdateInfo.mNumberOfCharactersToAdd    = numberOfCharactersInModel + maxSizeOfNewText;
390       textUpdateInfo.mClearAll                   = true;
391     }
392     else
393     {
394       textUpdateInfo.mCharacterIndex = std::min(cursorIndex, textUpdateInfo.mCharacterIndex);
395       textUpdateInfo.mNumberOfCharactersToAdd += maxSizeOfNewText;
396     }
397
398     if(impl.mMarkupProcessorEnabled)
399     {
400       InsertTextAnchor(controller, maxSizeOfNewText, cursorIndex);
401     }
402
403     // Update the cursor index.
404     cursorIndex += maxSizeOfNewText;
405
406     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, logicalModel->mText.Count(), eventData->mPrimaryCursorPosition);
407   }
408
409   if((0u == logicalModel->mText.Count()) &&
410      impl.IsPlaceholderAvailable())
411   {
412     // Show place-holder if empty after removing the pre-edit text
413     PlaceholderHandler::ShowPlaceholderText(impl);
414     eventData->mUpdateCursorPosition = true;
415     impl.ClearPreEditFlag();
416   }
417   else if(removedPrevious ||
418           removedSelected ||
419           (0 != utf32Characters.Count()))
420   {
421     // Queue an inserted event
422     impl.QueueModifyEvent(ModifyEvent::TEXT_INSERTED);
423
424     eventData->mUpdateCursorPosition = true;
425     if(removedSelected)
426     {
427       eventData->mScrollAfterDelete = true;
428     }
429     else
430     {
431       eventData->mScrollAfterUpdatePosition = true;
432     }
433   }
434
435   if(nullptr != impl.mEditableControlInterface)
436   {
437     impl.mEditableControlInterface->CursorPositionChanged(oldCursorPos, eventData->mPrimaryCursorPosition);
438   }
439
440   if(maxLengthReached)
441   {
442     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", logicalModel->mText.Count());
443
444     impl.ResetInputMethodContext();
445
446     if(NULL != impl.mEditableControlInterface)
447     {
448       // Do this last since it provides callbacks into application code
449       impl.mEditableControlInterface->MaxLengthReached();
450     }
451   }
452 }
453
454 void Controller::TextUpdater::PasteText(Controller& controller, const std::string& stringToPaste)
455 {
456   InsertText(controller, stringToPaste, Text::Controller::COMMIT);
457   Controller::Impl& impl = *controller.mImpl;
458   impl.ChangeState(EventData::EDITING);
459   impl.RequestRelayout();
460
461   if(NULL != impl.mEditableControlInterface)
462   {
463     // Do this last since it provides callbacks into application code
464     impl.mEditableControlInterface->TextChanged(true);
465   }
466 }
467
468 bool Controller::TextUpdater::RemoveText(
469   Controller&          controller,
470   int                  cursorOffset,
471   int                  numberOfCharacters,
472   UpdateInputStyleType type)
473 {
474   bool removed = false;
475
476   Controller::Impl& impl      = *controller.mImpl;
477   EventData*&       eventData = impl.mEventData;
478
479   if(nullptr == eventData)
480   {
481     return removed;
482   }
483
484   ModelPtr&        model        = impl.mModel;
485   LogicalModelPtr& logicalModel = model->mLogicalModel;
486   VisualModelPtr&  visualModel  = model->mVisualModel;
487
488   DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfCharacters %d\n", &controller, logicalModel->mText.Count(), eventData->mPrimaryCursorPosition, cursorOffset, numberOfCharacters);
489
490   if(!impl.IsShowingPlaceholderText())
491   {
492     // Delete at current cursor position
493     Vector<Character>& currentText         = logicalModel->mText;
494     CharacterIndex&    previousCursorIndex = eventData->mPrimaryCursorPosition;
495
496     CharacterIndex cursorIndex = 0;
497
498     // Validate the cursor position & number of characters
499     if((static_cast<int>(eventData->mPrimaryCursorPosition) + cursorOffset) >= 0)
500     {
501       cursorIndex = eventData->mPrimaryCursorPosition + cursorOffset;
502     }
503
504     //Handle Emoji clustering for cursor handling
505     // Deletion case: this is handling the deletion cases when the cursor is before or after Emoji
506     //  - Before: when use delete key and cursor is before Emoji (cursorOffset = -1)
507     //  - After: when use backspace key and cursor is after Emoji (cursorOffset = 0)
508
509     const Script script = logicalModel->GetScript(cursorIndex);
510     if((numberOfCharacters == 1u) &&
511        (IsOneOfEmojiScripts(script)))
512     {
513       //TODO: Use this clustering for Emoji cases only. This needs more testing to generalize to all scripts.
514       CharacterRun emojiClusteredCharacters = RetrieveClusteredCharactersOfCharacterIndex(visualModel, logicalModel, cursorIndex);
515       Length       actualNumberOfCharacters = emojiClusteredCharacters.numberOfCharacters;
516
517       //Set cursorIndex at the first characterIndex of clustred Emoji
518       cursorIndex = emojiClusteredCharacters.characterIndex;
519
520       numberOfCharacters = actualNumberOfCharacters;
521     }
522
523     if((cursorIndex + numberOfCharacters) > currentText.Count())
524     {
525       numberOfCharacters = currentText.Count() - cursorIndex;
526     }
527
528     TextUpdateInfo& textUpdateInfo = impl.mTextUpdateInfo;
529
530     if(eventData->mPreEditFlag || // If the preedit flag is enabled, it means two (or more) of them came together i.e. when two keys have been pressed at the same time.
531        ((cursorIndex + numberOfCharacters) <= textUpdateInfo.mPreviousNumberOfCharacters))
532     {
533       // Mark the paragraphs to be updated.
534       if(Layout::Engine::SINGLE_LINE_BOX == impl.mLayoutEngine.GetLayout())
535       {
536         textUpdateInfo.mCharacterIndex             = 0;
537         textUpdateInfo.mNumberOfCharactersToRemove = textUpdateInfo.mPreviousNumberOfCharacters;
538         textUpdateInfo.mNumberOfCharactersToAdd    = textUpdateInfo.mPreviousNumberOfCharacters - numberOfCharacters;
539         textUpdateInfo.mClearAll                   = true;
540       }
541       else
542       {
543         textUpdateInfo.mCharacterIndex = std::min(cursorIndex, textUpdateInfo.mCharacterIndex);
544         textUpdateInfo.mNumberOfCharactersToRemove += numberOfCharacters;
545       }
546
547       // Update the input style and remove the text's style before removing the text.
548
549       if(UPDATE_INPUT_STYLE == type)
550       {
551         InputStyle& eventDataInputStyle = eventData->mInputStyle;
552
553         // Keep a copy of the current input style.
554         InputStyle currentInputStyle;
555         currentInputStyle.Copy(eventDataInputStyle);
556
557         // Set first the default input style.
558         impl.RetrieveDefaultInputStyle(eventDataInputStyle);
559
560         // Update the input style.
561         logicalModel->RetrieveStyle(cursorIndex, eventDataInputStyle);
562
563         // Compare if the input style has changed.
564         const bool hasInputStyleChanged = !currentInputStyle.Equal(eventDataInputStyle);
565
566         if(hasInputStyleChanged)
567         {
568           const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(eventDataInputStyle);
569           // Queue the input style changed signal.
570           eventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
571         }
572       }
573
574       // If the number of current text and the number of characters to be deleted are same,
575       // it means all texts should be removed and all Preedit variables should be initialized.
576       if((currentText.Count() - numberOfCharacters == 0) && (cursorIndex == 0))
577       {
578         impl.ClearPreEditFlag();
579         textUpdateInfo.mNumberOfCharactersToAdd = 0;
580       }
581
582       // Updates the text style runs by removing characters. Runs with no characters are removed.
583       logicalModel->UpdateTextStyleRuns(cursorIndex, -numberOfCharacters);
584
585       // Remove the characters.
586       Vector<Character>::Iterator first = currentText.Begin() + cursorIndex;
587       Vector<Character>::Iterator last  = first + numberOfCharacters;
588
589       if(NULL != impl.mEditableControlInterface)
590       {
591         std::string utf8;
592         Utf32ToUtf8(first, numberOfCharacters, utf8);
593         impl.mEditableControlInterface->TextDeleted(cursorIndex, numberOfCharacters, utf8);
594       }
595
596       currentText.Erase(first, last);
597
598       if(impl.mMarkupProcessorEnabled)
599       {
600         RemoveTextAnchor(controller, cursorOffset, numberOfCharacters, previousCursorIndex);
601       }
602
603       if(nullptr != impl.mEditableControlInterface)
604       {
605         impl.mEditableControlInterface->CursorPositionChanged(previousCursorIndex, cursorIndex);
606       }
607
608       // Cursor position retreat
609       previousCursorIndex = cursorIndex;
610
611       eventData->mScrollAfterDelete = true;
612
613       if(EventData::INACTIVE == eventData->mState)
614       {
615         impl.ChangeState(EventData::EDITING);
616       }
617
618       DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", &controller, numberOfCharacters);
619       removed = true;
620     }
621   }
622
623   return removed;
624 }
625
626 bool Controller::TextUpdater::RemoveSelectedText(Controller& controller)
627 {
628   bool textRemoved(false);
629
630   Controller::Impl& impl = *controller.mImpl;
631
632   if(EventData::SELECTING == impl.mEventData->mState)
633   {
634     std::string removedString;
635     uint32_t    oldSelStart = impl.mEventData->mLeftSelectionPosition;
636     uint32_t    oldSelEnd   = impl.mEventData->mRightSelectionPosition;
637
638     impl.RetrieveSelection(removedString, true);
639
640     if(!removedString.empty())
641     {
642       textRemoved = true;
643       impl.ChangeState(EventData::EDITING);
644
645       if(impl.mMarkupProcessorEnabled)
646       {
647         int             cursorOffset        = -1;
648         int             numberOfCharacters  = removedString.length();
649         CharacterIndex& cursorIndex         = impl.mEventData->mPrimaryCursorPosition;
650         CharacterIndex  previousCursorIndex = cursorIndex + numberOfCharacters;
651
652         RemoveTextAnchor(controller, cursorOffset, numberOfCharacters, previousCursorIndex);
653       }
654
655       if(impl.mSelectableControlInterface != nullptr)
656       {
657         impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, impl.mEventData->mPrimaryCursorPosition, impl.mEventData->mPrimaryCursorPosition);
658       }
659     }
660   }
661
662   return textRemoved;
663 }
664
665 void Controller::TextUpdater::ResetText(Controller& controller)
666 {
667   Controller::Impl& impl         = *controller.mImpl;
668   LogicalModelPtr&  logicalModel = impl.mModel->mLogicalModel;
669
670   // Reset buffers.
671   logicalModel->mText.Clear();
672
673   // Reset the embedded images buffer.
674   logicalModel->ClearEmbeddedImages();
675
676   // Reset the anchors buffer.
677   logicalModel->ClearAnchors();
678
679   // We have cleared everything including the placeholder-text
680   impl.PlaceholderCleared();
681
682   impl.mTextUpdateInfo.mCharacterIndex             = 0u;
683   impl.mTextUpdateInfo.mNumberOfCharactersToRemove = impl.mTextUpdateInfo.mPreviousNumberOfCharacters;
684   impl.mTextUpdateInfo.mNumberOfCharactersToAdd    = 0u;
685
686   // Clear any previous text.
687   impl.mTextUpdateInfo.mClearAll = true;
688
689   // The natural size needs to be re-calculated.
690   impl.mRecalculateNaturalSize = true;
691
692   // The text direction needs to be updated.
693   impl.mUpdateTextDirection = true;
694
695   // Apply modifications to the model
696   impl.mOperationsPending = ALL_OPERATIONS;
697 }
698
699 void Controller::TextUpdater::InsertTextAnchor(Controller& controller, int numberOfCharacters, CharacterIndex previousCursorIndex)
700 {
701   Controller::Impl& impl         = *controller.mImpl;
702   ModelPtr&         model        = impl.mModel;
703   LogicalModelPtr&  logicalModel = model->mLogicalModel;
704
705   for(auto& anchor : logicalModel->mAnchors)
706   {
707     if(anchor.endIndex < previousCursorIndex) //      [anchor]  CUR
708     {
709       continue;
710     }
711     if(anchor.startIndex < previousCursorIndex) //      [anCURr]
712     {
713       anchor.endIndex += numberOfCharacters;
714     }
715     else // CUR  [anchor]
716     {
717       anchor.startIndex += numberOfCharacters;
718       anchor.endIndex += numberOfCharacters;
719     }
720     DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::InsertTextAnchor[%p] Anchor[%s] start[%d] end[%d]\n", &controller, anchor.href, anchor.startIndex, anchor.endIndex);
721   }
722 }
723
724 void Controller::TextUpdater::RemoveTextAnchor(Controller& controller, int cursorOffset, int numberOfCharacters, CharacterIndex previousCursorIndex)
725 {
726   Controller::Impl&        impl         = *controller.mImpl;
727   ModelPtr&                model        = impl.mModel;
728   LogicalModelPtr&         logicalModel = model->mLogicalModel;
729   Vector<Anchor>::Iterator it           = logicalModel->mAnchors.Begin();
730
731   while(it != logicalModel->mAnchors.End())
732   {
733     Anchor& anchor = *it;
734
735     if(anchor.endIndex <= previousCursorIndex && cursorOffset == 0) // [anchor]    CUR >>
736     {
737       // Nothing happens.
738     }
739     else if(anchor.endIndex <= previousCursorIndex && cursorOffset == -1) // [anchor] << CUR
740     {
741       int endIndex = anchor.endIndex;
742       int offset   = previousCursorIndex - endIndex;
743       int index    = endIndex - (numberOfCharacters - offset);
744
745       if(index < endIndex)
746       {
747         endIndex = index;
748       }
749
750       if((int)anchor.startIndex >= endIndex)
751       {
752         if(anchor.href)
753         {
754           delete[] anchor.href;
755         }
756         it = logicalModel->mAnchors.Erase(it);
757         continue;
758       }
759       else
760       {
761         anchor.endIndex = endIndex;
762       }
763     }
764     else if(anchor.startIndex >= previousCursorIndex && cursorOffset == -1) // << CUR    [anchor]
765     {
766       anchor.startIndex -= numberOfCharacters;
767       anchor.endIndex -= numberOfCharacters;
768     }
769     else if(anchor.startIndex >= previousCursorIndex && cursorOffset == 0) //    CUR >> [anchor]
770     {
771       int startIndex = anchor.startIndex;
772       int endIndex   = anchor.endIndex;
773       int index      = previousCursorIndex + numberOfCharacters - 1;
774
775       if(startIndex > index)
776       {
777         anchor.startIndex -= numberOfCharacters;
778         anchor.endIndex -= numberOfCharacters;
779       }
780       else if(endIndex > index + 1)
781       {
782         anchor.endIndex -= numberOfCharacters;
783       }
784       else
785       {
786         if(anchor.href)
787         {
788           delete[] anchor.href;
789         }
790         it = logicalModel->mAnchors.Erase(it);
791         continue;
792       }
793     }
794     else if(cursorOffset == -1) // [<< CUR]
795     {
796       int startIndex = anchor.startIndex;
797       int index      = previousCursorIndex - numberOfCharacters;
798
799       if(startIndex >= index)
800       {
801         anchor.startIndex = index;
802       }
803       anchor.endIndex -= numberOfCharacters;
804     }
805     else if(cursorOffset == 0) // [CUR >>]
806     {
807       anchor.endIndex -= numberOfCharacters;
808     }
809     else
810     {
811       // When this condition is reached, someting is wrong.
812       DALI_LOG_ERROR("Controller::RemoveTextAnchor[%p] Invaild state cursorOffset[%d]\n", &controller, cursorOffset);
813     }
814
815     DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::RemoveTextAnchor[%p] Anchor[%s] start[%d] end[%d]\n", &controller, anchor.href, anchor.startIndex, anchor.endIndex);
816
817     it++;
818   }
819 }
820
821 } // namespace Text
822
823 } // namespace Toolkit
824
825 } // namespace Dali