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