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