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