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