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