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