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