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