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