Merge branch 'tizen' into new_text
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / text-view / text-view-processor.cpp
1 /*
2  * Copyright (c) 2014 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 // FILE HEADER
19 #include <dali-toolkit/internal/controls/text-view/text-view-processor.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali-toolkit/internal/controls/text-view/text-view-word-processor.h>
24 #include <dali-toolkit/internal/controls/text-view/text-view-paragraph-processor.h>
25 #include <dali-toolkit/internal/controls/text-view/text-view-processor-helper-functions.h>
26 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
27 #include <dali-toolkit/internal/controls/text-view/text-processor-bidirectional-info.h>
28
29 namespace Dali
30 {
31
32 namespace Toolkit
33 {
34
35 namespace Internal
36 {
37
38 namespace TextViewProcessor
39 {
40
41 namespace
42 {
43 /**
44  * Update text layout info.
45  *
46  * Updates the size of the whole text, the maximum width of all words and the number of characters.
47  *
48  * @param[in,out] textLayoutInfo
49  */
50 void UpdateLayoutInfo( TextLayoutInfo& textLayoutInfo )
51 {
52   // Initialize members to be updated.
53   textLayoutInfo.mWholeTextSize = Size::ZERO;
54   textLayoutInfo.mMaxWordWidth = 0.f;
55   textLayoutInfo.mNumberOfCharacters = 0u;
56
57   // Traverse all text updating values.
58   for( ParagraphLayoutInfoContainer::const_iterator paragraphIt = textLayoutInfo.mParagraphsLayoutInfo.begin(), paragraphEndIt = textLayoutInfo.mParagraphsLayoutInfo.end();
59        paragraphIt != paragraphEndIt;
60        ++paragraphIt )
61   {
62     const ParagraphLayoutInfo& paragraph( *paragraphIt );
63
64     // Updates text size with the size of all paragraphs.
65     UpdateSize( textLayoutInfo.mWholeTextSize, paragraph.mSize, GrowHeight );
66
67     // Updates number of characters.
68     textLayoutInfo.mNumberOfCharacters += paragraph.mNumberOfCharacters;
69
70     // Updates the max word's width.
71     for( WordLayoutInfoContainer::const_iterator wordIt = paragraph.mWordsLayoutInfo.begin(), wordEndIt = paragraph.mWordsLayoutInfo.end();
72          wordIt != wordEndIt;
73          ++wordIt )
74     {
75       const WordLayoutInfo& word( *wordIt );
76
77       textLayoutInfo.mMaxWordWidth = std::max( textLayoutInfo.mMaxWordWidth, word.mSize.width );
78     }
79   }
80 }
81
82 } // namespace
83
84 // Constructors and assignment operators
85
86 TextInfoIndices::TextInfoIndices()
87 : mParagraphIndex( 0u ),
88   mWordIndex( 0u ),
89   mCharacterIndex( 0u ),
90   mCharacterParagraphIndex( 0u )
91 {
92 }
93
94 TextInfoIndices::TextInfoIndices( const std::size_t paragraphIndex,
95                                   const std::size_t wordIndex,
96                                   const std::size_t characterIndex )
97 : mParagraphIndex( paragraphIndex ),
98   mWordIndex( wordIndex ),
99   mCharacterIndex( characterIndex ),
100   mCharacterParagraphIndex( 0u )
101 {
102 }
103
104 bool TextInfoIndices::operator==( const TextInfoIndices& indices ) const
105 {
106   return ( ( mParagraphIndex == indices.mParagraphIndex ) &&
107            ( mWordIndex == indices.mWordIndex ) &&
108            ( mCharacterIndex == indices.mCharacterIndex ) &&
109            ( mCharacterParagraphIndex == indices.mCharacterParagraphIndex ) );
110 }
111
112 /////////////////////
113 // Layout info.
114 /////////////////////
115
116 TextLayoutInfo::TextLayoutInfo()
117 : mWholeTextSize(),
118   mMaxWordWidth( 0.f ),
119   mMaxItalicsOffset( 0.f ),
120   mNumberOfCharacters( 0u ),
121   mParagraphsLayoutInfo(),
122   mEllipsizeLayoutInfo(),
123   mEllipsisText( "..." ),
124   mEllipsisTextStyles()
125 {
126   // Sets default styles for the default ellipsis text.
127   mEllipsisTextStyles.PushBack( new TextStyle() );
128   mEllipsisTextStyles.PushBack( new TextStyle() );
129   mEllipsisTextStyles.PushBack( new TextStyle() );
130 }
131
132 TextLayoutInfo::~TextLayoutInfo()
133 {
134   ClearStyles();
135 }
136
137 TextLayoutInfo::TextLayoutInfo( const TextLayoutInfo& text )
138 : mWholeTextSize( text.mWholeTextSize ),
139   mMaxWordWidth( text.mMaxWordWidth ),
140   mMaxItalicsOffset( text.mMaxItalicsOffset ),
141   mNumberOfCharacters( text.mNumberOfCharacters ),
142   mParagraphsLayoutInfo( text.mParagraphsLayoutInfo ),
143   mEllipsizeLayoutInfo( text.mEllipsizeLayoutInfo ),
144   mEllipsisText( text.mEllipsisText ),
145   mEllipsisTextStyles()
146 {
147   for( Vector<TextStyle*>::ConstIterator it = text.mEllipsisTextStyles.Begin(), endIt = text.mEllipsisTextStyles.End(); it != endIt; ++it )
148   {
149     mEllipsisTextStyles.PushBack( new TextStyle( *(*it) ) );
150   }
151 }
152
153 TextLayoutInfo& TextLayoutInfo::operator=( const TextLayoutInfo& text )
154 {
155   if( this != &text )
156   {
157     mWholeTextSize = text.mWholeTextSize;
158     mMaxWordWidth = text.mMaxWordWidth;
159     mMaxItalicsOffset = text.mMaxItalicsOffset;
160     mNumberOfCharacters = text.mNumberOfCharacters;
161     mParagraphsLayoutInfo = text.mParagraphsLayoutInfo;
162     mEllipsizeLayoutInfo = text.mEllipsizeLayoutInfo;
163     mEllipsisText = text.mEllipsisText;
164
165     ClearStyles();
166
167     for( Vector<TextStyle*>::ConstIterator it = text.mEllipsisTextStyles.Begin(), endIt = text.mEllipsisTextStyles.End(); it != endIt; ++it )
168     {
169       mEllipsisTextStyles.PushBack( new TextStyle( *(*it) ) );
170     }
171   }
172   return *this;
173 }
174
175 void TextLayoutInfo::ClearStyles()
176 {
177   for( Vector<TextStyle*>::Iterator it = mEllipsisTextStyles.Begin(), endIt = mEllipsisTextStyles.End(); it != endIt; ++it )
178   {
179     delete *it;
180   }
181   mEllipsisTextStyles.Clear();
182 }
183
184 /////////////////////////////////////////////////////////////////////////////////////////////
185
186 void CreateTextInfo( const MarkupProcessor::StyledTextArray& text,
187                      const TextView::LayoutParameters& layoutParameters,
188                      TextView::RelayoutData& relayoutData )
189 {
190   // * Traverse the given text spliting it in paragraphs and each paragraph in words.
191   // * White spaces and new paragraph characters are alone in one word.
192   // * Bidirectional text is processed in each paragraph.
193   //     It creates the conversion tables
194   //     It does the ligatures if there is arabic glyphs.
195   //     It doesn't reorder the text as it must be done for each line not for the paragraph.
196   // * Generates a layout data structure to store layout information (size, position, ascender, text direction, etc) and metrics of all characters.
197   // * Store text for each paragraph and the style for each character.
198
199   // Collect previously created text-actors.
200   std::vector<TextActor> textActors;
201   CollectTextActorsFromParagraphs( textActors, relayoutData.mTextLayoutInfo, 0u, relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.size() );
202
203   if( !textActors.empty() )
204   {
205     // Add text-actors to the cache.
206     relayoutData.mTextActorCache.InsertTextActors( textActors );
207     relayoutData.mTextActorCache.ClearTexts();
208   }
209
210   // Store the ellipsize layout info before clearing the previous created info.
211   // TODO: fix this. Don't clear the ellipsis layout, store it somewhere else ...
212   const WordLayoutInfo ellipsizeInfo = relayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo;
213   Dali::Text           ellipsisText = relayoutData.mTextLayoutInfo.mEllipsisText;
214   Vector<TextStyle*>   ellipsisTextStyles = relayoutData.mTextLayoutInfo.mEllipsisTextStyles;
215   relayoutData.mTextLayoutInfo.mEllipsisTextStyles.Clear();
216
217   // clear previously created info.
218   relayoutData.mTextLayoutInfo = TextLayoutInfo();
219   relayoutData.mCharacterLogicalToVisualMap.clear();
220   relayoutData.mCharacterVisualToLogicalMap.clear();
221
222   // Sets the ellipsize layout info.
223   relayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo = ellipsizeInfo;
224   relayoutData.mTextLayoutInfo.mEllipsisText = ellipsisText;
225   relayoutData.mTextLayoutInfo.mEllipsisTextStyles = ellipsisTextStyles;
226
227   // Split the whole text in paragraphs.
228   // It returns a vector of Text with all the paragraphs.
229   // and for each paragraph a vector of styles per character. TODO: don't create a style per character.
230   std::vector<Text> paragraphs;
231   std::vector< Vector<TextStyle*> > styles;
232   TextProcessor::SplitInParagraphs( text,
233                                     paragraphs,
234                                     styles );
235
236   // Reserve space for the current number of paragraphs.
237   relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.resize( paragraphs.size(), ParagraphLayoutInfo() );
238
239   // Traverse all paragraphs
240   std::size_t paragraphIndex = 0u;
241   std::vector<Text>::iterator paragraphIt, paragraphEndIt;
242   std::vector< Vector<TextStyle*> >::const_iterator styleIt, styleEndIt;
243   for( paragraphIt = paragraphs.begin(), paragraphEndIt = paragraphs.end(),
244          styleIt = styles.begin(), styleEndIt = styles.end();
245        ( paragraphIt != paragraphEndIt ) && ( styleIt != styleEndIt );
246        ++paragraphIndex,
247          ++paragraphIt,
248          ++styleIt )
249   {
250     // Gets the paragraph and the styles for each character.
251     Text& paragraph( *paragraphIt );
252     const Vector<TextStyle*>& textStyles( *styleIt );
253
254     // Retrieve the data structure for the current paragraph.
255     ParagraphLayoutInfo& paragraphLayoutInfo = *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + paragraphIndex );
256
257     // Sets text and styles.
258     paragraphLayoutInfo.mText = paragraph;
259     paragraphLayoutInfo.mTextStyles = textStyles;
260
261     // Fills the paragraph data structure with the layout info.
262
263     if( !paragraph.IsEmpty() )
264     {
265       CreateParagraphInfo( relayoutData,
266                            paragraphLayoutInfo );
267
268       // do not add the line offset if the paragraph has no characters.
269       paragraphLayoutInfo.mSize.height += layoutParameters.mLineHeightOffset;
270       paragraphLayoutInfo.mLineHeightOffset = layoutParameters.mLineHeightOffset;
271     }
272     else
273     {
274       // This paragraph has no text. i.e. the previous paragraph ends with '\n'.
275       // Even though, line height needs to be set in order to align the whole text correctly.
276
277       float lineHeight = 0.f;
278       // Get the last character of the last paragraph.
279       if( 0u < paragraphIndex )
280       {
281         const ParagraphLayoutInfo& paragraphInfo( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + ( paragraphIndex - 1u ) ) );
282
283         const CharacterLayoutInfo characterInfo = GetLastCharacterLayoutInfo( paragraphInfo );
284
285         lineHeight = characterInfo.mSize.height;
286       }
287
288       paragraphLayoutInfo.mSize.height = lineHeight;
289     }
290
291     // Fills the conversion tables used to get the logical or visual position of a character is case there is right to left text.
292     // If there is right to left text, it needs to be updated every time the text is relaid-out.
293     for( std::size_t index = 0u; index < paragraphLayoutInfo.mNumberOfCharacters; ++index )
294     {
295       relayoutData.mCharacterLogicalToVisualMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
296       relayoutData.mCharacterVisualToLogicalMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
297     }
298
299     // Update layout info for the whole text.
300     UpdateSize( relayoutData.mTextLayoutInfo.mWholeTextSize, paragraphLayoutInfo.mSize, GrowHeight );
301     relayoutData.mTextLayoutInfo.mNumberOfCharacters += paragraphLayoutInfo.mNumberOfCharacters;
302   } // end of paragraphs
303 }
304
305 void UpdateTextInfo( std::size_t position,
306                      const MarkupProcessor::StyledTextArray& text,
307                      const TextView::LayoutParameters& layoutParameters,
308                      TextView::RelayoutData& relayoutData )
309 {
310   // Update current internal data structure with added text.
311
312   // * Creates layout info for the given text.
313   // * With the given position, find where to add the text.
314   // * If the new text is not added at the end of current text, a paragraph need to be split.
315   // * Merge the last paragraph of the new text to the last part or the split paragraph.
316   // * Add paragraphs between first and last of the new text.
317   // * Merge the first part of the split paragraph with the first paragraph of the new text.
318   // * Update the layout info for the whole text.
319
320   // Early returns:
321
322   if( text.empty() )
323   {
324     // nothing to do if the input text is empty.
325     return;
326   }
327
328   if( 0u == relayoutData.mTextLayoutInfo.mNumberOfCharacters )
329   {
330     // Current text is empty. There is no need to update current data structure,
331     // just create a new one with the new input text.
332     CreateTextInfo( text,
333                     layoutParameters,
334                     relayoutData );
335     return;
336   }
337
338   // initial checks.
339   if( position > relayoutData.mTextLayoutInfo.mNumberOfCharacters )
340   {
341     // Asserts if text is to be added out of bounds.
342     DALI_ASSERT_ALWAYS( !"TextViewProcessor::UpdateTextInfo (insert). Trying to insert text out of bounds." );
343   }
344
345
346   // TODO
347   // If in the insertion is involved right to left text,
348   // check the neighbour characters is needed.
349   //   i.e. Arabic characters may have four different glyphs (isolated, end, middle, begin).
350   //        So check the neighbours on each side is needed in order to render
351   //        the correct ligatures.
352
353
354   TextView::RelayoutData relayoutDataForNewText;
355
356   // Creates layout info for the given text.
357   // It doesn't create text-actors as text could be added to an existing one.
358   CreateTextInfo( text,
359                   layoutParameters,
360                   relayoutDataForNewText );
361
362   // Fills the conversion tables used to get the logical or visual position of a character is case there is right to left text.
363   // If there is right to left text, it needs to be updated every time the text is relaid-out.
364   std::size_t index = 0u;
365   for( std::size_t i = 0u; i < relayoutDataForNewText.mTextLayoutInfo.mNumberOfCharacters; ++i )
366   {
367     relayoutData.mCharacterLogicalToVisualMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
368     relayoutData.mCharacterVisualToLogicalMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
369     ++index;
370   }
371
372   // If a paragraph is split, it stores the last part of the paragraph.
373   ParagraphLayoutInfo lastParagraphLayoutInfo;
374
375   // Stores indices to the paragraph, word and character of the given position.
376   TextInfoIndices textInfoIndices;
377
378   if( position < relayoutData.mTextLayoutInfo.mNumberOfCharacters )
379   {
380     // Get paragraph, word and character indices for given position.
381     GetIndicesFromGlobalCharacterIndex( position,
382                                         relayoutData.mTextLayoutInfo,
383                                         textInfoIndices );
384
385     // 1) Split the paragraph
386
387     // Split a paragraph in two is needed, then merge the first part of the split paragraph
388     // with the first paragraph of the new text, add subsequent paragraphs and merge the last paragraph
389     // of the new text with the last part of the split paragraph.
390
391     // Implementation notes!
392     //
393     // These references to the first paragraph are declared in this scope because if new paragraphs are inserted in step 2,
394     // they become invalid, making the algorithm to crash if used again.
395     // In the step 3, references to the first paragraph are needed and declared again.
396
397     // Stores the first part of the split paragraph.
398     ParagraphLayoutInfo& firstParagraphLayoutInfo( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndices.mParagraphIndex ) );
399
400     SplitParagraph( textInfoIndices,
401                     PointSize( layoutParameters.mLineHeightOffset ),
402                     firstParagraphLayoutInfo,
403                     lastParagraphLayoutInfo );
404   }
405   else
406   {
407     // Position is just after the last character.
408     // Calculates indices for that position.
409     if( !relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.empty() )
410     {
411       textInfoIndices.mParagraphIndex = relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.size() - 1u;
412       const ParagraphLayoutInfo& paragraphLayoutInfo( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.end() - 1u ) );
413
414       if( !paragraphLayoutInfo.mWordsLayoutInfo.empty() )
415       {
416         textInfoIndices.mWordIndex = paragraphLayoutInfo.mWordsLayoutInfo.size() - 1u;
417
418         const WordLayoutInfo& wordLayoutInfo( *( paragraphLayoutInfo.mWordsLayoutInfo.end() - 1u ) );
419         textInfoIndices.mCharacterIndex = wordLayoutInfo.mCharactersLayoutInfo.size();
420
421         // Sets the character index within the paragraph.
422         textInfoIndices.mCharacterParagraphIndex = 0u;
423         for( WordLayoutInfoContainer::const_iterator it = paragraphLayoutInfo.mWordsLayoutInfo.begin(), endIt = paragraphLayoutInfo.mWordsLayoutInfo.end(); it != endIt; ++it )
424         {
425           textInfoIndices.mCharacterParagraphIndex += wordLayoutInfo.mCharactersLayoutInfo.size();
426         }
427       }
428     }
429   }
430
431   // 2) If the new text has more than 1 paragraph, merge the last paragraph of the input text with the last part of the split paragraph.
432   //TODO check this cases ( num paragraphs ==1, >1, >2 ) if it could be simplified.
433   if( relayoutDataForNewText.mTextLayoutInfo.mParagraphsLayoutInfo.size() > 1u )
434   {
435     ParagraphLayoutInfo& lastInputParagraphLayoutInfo( *( relayoutDataForNewText.mTextLayoutInfo.mParagraphsLayoutInfo.end() - 1u ) );
436
437     MergeParagraph( lastInputParagraphLayoutInfo,
438                     lastParagraphLayoutInfo );
439
440     if( relayoutDataForNewText.mTextLayoutInfo.mParagraphsLayoutInfo.size() > 2u )
441     {
442       // Insert all paragraphs except first and last in the text.
443       relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.insert( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndices.mParagraphIndex + 1u,
444                                                             relayoutDataForNewText.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + 1u,
445                                                             relayoutDataForNewText.mTextLayoutInfo.mParagraphsLayoutInfo.end() - 1u );
446     }
447
448     // Insert the last paragraph to the text
449     relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.insert( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndices.mParagraphIndex + relayoutDataForNewText.mTextLayoutInfo.mParagraphsLayoutInfo.size() - 1u,
450                                                           lastInputParagraphLayoutInfo );
451   }
452   else
453   {
454     // Merge the new paragraph to the last part of the split paragraph.
455     ParagraphLayoutInfo& inputParagraphLayoutInfo( *relayoutDataForNewText.mTextLayoutInfo.mParagraphsLayoutInfo.begin() );
456
457     MergeParagraph( inputParagraphLayoutInfo,
458                     lastParagraphLayoutInfo );
459   }
460
461   // 3) Merge the first paragraph of the split text with the first paragraph of the input text.
462   ParagraphLayoutInfo& firstParagraphLayoutInfo( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndices.mParagraphIndex ) );
463   ParagraphLayoutInfo& firstInputParagraphLayoutInfo( *relayoutDataForNewText.mTextLayoutInfo.mParagraphsLayoutInfo.begin() );
464
465   MergeParagraph( firstParagraphLayoutInfo,
466                   firstInputParagraphLayoutInfo );
467
468   // 4) Update text info.
469
470   // Updates the whole text size, maximum word size, etc.
471   UpdateLayoutInfo( relayoutData.mTextLayoutInfo );
472 }
473
474 void UpdateTextInfo( std::size_t position,
475                      std::size_t numberOfCharacters,
476                      const TextView::LayoutParameters& layoutParameters,
477                      TextView::RelayoutData& relayoutData,
478                      const TextOperationOnRemove clearText )
479 {
480   // Removes 'numberOfCharacters' starting from 'position'.
481
482   // * It checks if text to be deleted is in the same paragraph or not:
483   // *   If it is not, check which paragraphs need to be split/merged or deleted.
484   // *   If it is but all characters of the paragraph are going to be deleted, just delete the paragraph (nothing needs to be split/merged)
485   // *   If only some characters of the same paragraph are going to be deleted, proceed similarly: check if text to be deleted is in the same word.
486   // *     If it is not, split/merge words.
487   // *     Check if the whole word needs to be deleted.
488   // *     Check if only some characters of the word need to be deleted.
489   // * Updates layout info.
490
491   // * The algorithm checks if a word separator is deleted (in that case, different words need to be merged) and if a new paragraph separator is deleted (two paragraphs need to be merged).
492
493   // Early return
494
495   if( 0u == numberOfCharacters )
496   {
497     DALI_ASSERT_DEBUG( !"TextViewProcessor::UpdateTextInfo. WARNING: trying to delete 0 characters!" );
498
499     // nothing to do if no characters are deleted.
500     return;
501   }
502
503   // Asserts if trying to delete text out of bounds.
504   DALI_ASSERT_ALWAYS( position + numberOfCharacters <= relayoutData.mTextLayoutInfo.mNumberOfCharacters && "TextViewProcessor::UpdateTextInfo. ERROR: trying to delete characters out of boundary" );
505
506   // Remove characters from character to visual map and vs.
507   // If there is right to left text, it needs to be updated every time the text is relaid-out.
508   relayoutData.mCharacterLogicalToVisualMap.erase( relayoutData.mCharacterLogicalToVisualMap.end() - numberOfCharacters, relayoutData.mCharacterLogicalToVisualMap.end() );
509   relayoutData.mCharacterVisualToLogicalMap.erase( relayoutData.mCharacterVisualToLogicalMap.end() - numberOfCharacters, relayoutData.mCharacterVisualToLogicalMap.end() );
510
511   // Get paragraph, word and character indices for the given start position.
512   TextInfoIndices textInfoIndicesBegin;
513   GetIndicesFromGlobalCharacterIndex( position,
514                                       relayoutData.mTextLayoutInfo,
515                                       textInfoIndicesBegin );
516
517   // Get paragraph, word and character indices for the given end position (start position + number of characters to be deleted).
518   TextInfoIndices textInfoIndicesEnd;
519   GetIndicesFromGlobalCharacterIndex( position + numberOfCharacters - 1u,
520                                       relayoutData.mTextLayoutInfo,
521                                       textInfoIndicesEnd );
522
523   // Vectors used to temporary store text-actors removed from text.
524   // Three vectors are needed because text-actors are not removed in order
525   // but insert them in order is required to reuse them later.
526   std::vector<TextActor> removedTextActorsFromBegin;
527   std::vector<TextActor> removedTextActorsFromMid;
528   std::vector<TextActor> removedTextActorsFromEnd;
529
530   // Whether paragraphs and words need to be merged.
531   bool mergeParagraphs = false;
532   bool mergeWords = false;
533
534   // Indices of the paragraphs and words to be merged.
535   TextInfoIndices textInfoMergeIndicesBegin;
536   TextInfoIndices textInfoMergeIndicesEnd;
537
538   const ParagraphLayoutInfo& paragraphLayout( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndicesBegin.mParagraphIndex ) ); // used to check the number of characters of the paragraph
539                                                                                                                                     // if all characters to be deleted are in the same paragraph.
540   if( textInfoIndicesBegin.mParagraphIndex < textInfoIndicesEnd.mParagraphIndex )
541   {
542     // Deleted text is from different paragraphs. It may need to split two paragraphs, and merge first part of the first one with last part of the last one.
543
544     // whether first or last paragraph need to be split and merged with the last part.
545     bool mergeFirstParagraph = false;
546     bool mergeLastParagraph = true;
547
548     textInfoMergeIndicesBegin.mParagraphIndex = textInfoIndicesBegin.mParagraphIndex;
549     textInfoMergeIndicesEnd.mParagraphIndex = textInfoIndicesEnd.mParagraphIndex;
550
551     if( ( textInfoIndicesBegin.mWordIndex > 0u ) || ( textInfoIndicesBegin.mCharacterIndex > 0u ) )
552     {
553       // first character to be deleted is not the first one of the current paragraph.
554       ++textInfoIndicesBegin.mParagraphIndex; // won't delete current paragraph
555
556       // As some characters remain, this paragraph could be merged with the last one.
557       mergeFirstParagraph = true;
558     }
559
560     // Check if all characters of the last paragraph are going to be deleted.
561     bool wholeParagraphDeleted = false;
562     const ParagraphLayoutInfo& lastParagraphLayout( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndicesEnd.mParagraphIndex ) );
563     if( textInfoIndicesEnd.mWordIndex + 1u == lastParagraphLayout.mWordsLayoutInfo.size() )
564     {
565       const WordLayoutInfo& lastWordLayout( *( lastParagraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex ) );
566       if( textInfoIndicesEnd.mCharacterIndex + 1u == lastWordLayout.mCharactersLayoutInfo.size() )
567       {
568         // All characters of the last paragraph are going to be deleted.
569         ++textInfoIndicesEnd.mParagraphIndex; // will delete the last paragraph.
570
571         // the whole last paragraph is deleted. Need to check if the next paragraph could be merged.
572         mergeLastParagraph = false;
573         wholeParagraphDeleted = true;
574       }
575     }
576
577     if( wholeParagraphDeleted )
578     {
579       // It means the whole last paragraph is deleted completely.
580       // It's needed to check if there is another paragraph after that could be merged.
581       if( textInfoIndicesEnd.mParagraphIndex < relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.size() )
582       {
583         mergeLastParagraph = true;
584
585         // Point the first characters of the next paragraph.
586         textInfoIndicesEnd.mWordIndex = 0u;
587         textInfoIndicesEnd.mCharacterIndex = 0u;
588         textInfoMergeIndicesEnd.mParagraphIndex = textInfoIndicesEnd.mParagraphIndex;
589       }
590     }
591
592     // If some characters remain in the first and last paragraph, they need to be merged.
593     mergeParagraphs = mergeFirstParagraph && mergeLastParagraph;
594
595     if( mergeParagraphs )
596     {
597       // last paragraph is going to be merged with the first one, so is not needed.
598       ++textInfoIndicesEnd.mParagraphIndex; // will delete the last paragraph.
599     }
600
601     if( mergeFirstParagraph )
602     {
603       // Remove characters from the first paragraph.
604
605       // Vectors used to temporary store text-actors removed from the paragraph.
606       // Three vectors are needed because text-actors are not removed in order
607       // but insert them in order is required to reuse them later.
608       std::vector<TextActor> removedTextActorsFromFirstWord;
609       std::vector<TextActor> removedTextActorsFromFirstParagraph;
610
611       // As paragraphIndexBegin has been increased just to not to remove the paragraph, decrease now is needed to access it.
612       ParagraphLayoutInfo& paragraphLayout( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndicesBegin.mParagraphIndex - 1u ) );
613
614       // Remove the characters from the text and the styles.
615       paragraphLayout.mText.Remove( textInfoIndicesBegin.mCharacterParagraphIndex, paragraphLayout.mNumberOfCharacters - textInfoIndicesBegin.mCharacterParagraphIndex );
616
617       for( Vector<TextStyle*>::Iterator it = paragraphLayout.mTextStyles.Begin() + textInfoIndicesBegin.mCharacterParagraphIndex,
618              endIt = paragraphLayout.mTextStyles.End();
619            it != endIt;
620            ++it )
621       {
622         delete *it;
623       }
624       paragraphLayout.mTextStyles.Erase( paragraphLayout.mTextStyles.Begin() + textInfoIndicesBegin.mCharacterParagraphIndex,
625                                          paragraphLayout.mTextStyles.End() );
626
627       if( ( textInfoIndicesBegin.mWordIndex + 1u < paragraphLayout.mWordsLayoutInfo.size() ) || ( 0u == textInfoIndicesBegin.mCharacterIndex ) )
628       {
629         // Remove extra words within current paragraph. (and current word if whole characters are removed)
630         // 0 == characterIndexBegin means the whole word is deleted.
631         const std::size_t wordIndex = ( ( 0u == textInfoIndicesBegin.mCharacterIndex ) ? textInfoIndicesBegin.mWordIndex : textInfoIndicesBegin.mWordIndex + 1u );
632
633         // Store text-actors before removing them.
634         CollectTextActorsFromWords( removedTextActorsFromFirstParagraph, paragraphLayout, wordIndex, paragraphLayout.mWordsLayoutInfo.size() );
635
636         RemoveWordsFromParagraph( wordIndex,
637                                   paragraphLayout.mWordsLayoutInfo.size() - wordIndex,
638                                   layoutParameters.mLineHeightOffset,
639                                   paragraphLayout );
640       }
641
642       if( ( textInfoIndicesBegin.mWordIndex < paragraphLayout.mWordsLayoutInfo.size() ) && ( textInfoIndicesBegin.mCharacterIndex > 0u ) )
643       {
644         // Only some characters of the word need to be removed.
645         WordLayoutInfo& wordLayout( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex ) );
646
647         // Store text-actors before removing them.
648         CollectTextActors( removedTextActorsFromFirstWord, wordLayout, textInfoIndicesBegin.mCharacterIndex, wordLayout.mCharactersLayoutInfo.size() );
649
650         const std::size_t wordNumberCharacters = wordLayout.mCharactersLayoutInfo.size();
651         RemoveCharactersFromWord( textInfoIndicesBegin.mCharacterIndex,
652                                   wordLayout.mCharactersLayoutInfo.size() - textInfoIndicesBegin.mCharacterIndex,
653                                   wordLayout );
654
655         // discount the removed number of characters.
656         const std::size_t removedNumberOfCharacters = ( wordNumberCharacters - wordLayout.mCharactersLayoutInfo.size() );
657         paragraphLayout.mNumberOfCharacters -= removedNumberOfCharacters;
658       }
659       UpdateLayoutInfo( paragraphLayout, layoutParameters.mLineHeightOffset );
660
661       // Insert the text-actors in order.
662       removedTextActorsFromBegin.insert( removedTextActorsFromBegin.end(), removedTextActorsFromFirstWord.begin(), removedTextActorsFromFirstWord.end() );
663       removedTextActorsFromBegin.insert( removedTextActorsFromBegin.end(), removedTextActorsFromFirstParagraph.begin(), removedTextActorsFromFirstParagraph.end() );
664     }
665
666     if( mergeLastParagraph && !wholeParagraphDeleted )
667     {
668       // Some characters from the last paragraph need to be removed.
669
670       // Vectors used to temporary store text-actors removed from the paragraph.
671       // Three vectors are needed because text-actors are not removed in order
672       // but insert them in order is required to reuse them later.
673       std::vector<TextActor> removedTextActorsFromFirstWord;
674       std::vector<TextActor> removedTextActorsFromFirstParagraph;
675
676       // paragraphIndexEnd was increased to delete the last paragraph if paragraphs need to be merged.
677       // To access now the last paragraph we need to decrease the index.
678       const std::size_t paragraphIndex = ( mergeParagraphs ? textInfoIndicesEnd.mParagraphIndex - 1u : textInfoIndicesEnd.mParagraphIndex );
679
680       // Get the last paragraph.
681       ParagraphLayoutInfo& paragraphLayout( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + paragraphIndex ) );
682
683       // Remove the characters from the text and the styles.
684       const std::size_t lastCharacterIndex = textInfoIndicesEnd.mCharacterParagraphIndex + 1u;
685
686       paragraphLayout.mText.Remove( 0u, lastCharacterIndex );
687
688       for( Vector<TextStyle*>::Iterator it = paragraphLayout.mTextStyles.Begin(),
689              endIt = paragraphLayout.mTextStyles.Begin() + lastCharacterIndex;
690            it != endIt;
691            ++it )
692       {
693         delete *it;
694       }
695       paragraphLayout.mTextStyles.Erase( paragraphLayout.mTextStyles.Begin(),
696                                          paragraphLayout.mTextStyles.Begin() + lastCharacterIndex );
697
698       // Check if is needed remove the whole word. (If the character index is pointing just after the end of the word)
699       const WordLayoutInfo& wordLayout( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex ) );
700       bool removeWholeWord = wordLayout.mCharactersLayoutInfo.size() == textInfoIndicesEnd.mCharacterIndex + 1u;
701
702       if( ( textInfoIndicesEnd.mWordIndex > 0u ) || ( removeWholeWord ) )
703       {
704         // Store text-actors before removing them.
705         CollectTextActorsFromWords( removedTextActorsFromFirstParagraph,
706                                     paragraphLayout,
707                                     0u,
708                                     ( removeWholeWord ) ? textInfoIndicesEnd.mWordIndex + 1u : textInfoIndicesEnd.mWordIndex );
709
710         // Remove extra words. (and current word if whole characters are removed)
711         RemoveWordsFromParagraph( 0u,
712                                   ( removeWholeWord ) ? textInfoIndicesEnd.mWordIndex + 1u : textInfoIndicesEnd.mWordIndex,
713                                   layoutParameters.mLineHeightOffset,
714                                   paragraphLayout );
715       }
716
717       if( !removeWholeWord )
718       {
719         // Only some characters of the word need to be deleted.
720
721         // After removing all extra words. The word with the characters to be removed is the first one.
722         WordLayoutInfo& wordLayout( *paragraphLayout.mWordsLayoutInfo.begin() );
723
724         // Store text-actors before removing them.
725         CollectTextActors( removedTextActorsFromFirstWord, wordLayout, 0u, textInfoIndicesEnd.mCharacterIndex + 1u );
726
727         const std::size_t wordNumberCharacters = wordLayout.mCharactersLayoutInfo.size();
728         RemoveCharactersFromWord( 0u,
729                                   textInfoIndicesEnd.mCharacterIndex + 1u,
730                                   wordLayout );
731
732         // discount the removed number of characters.
733         const std::size_t removedNumberOfCharacters = ( wordNumberCharacters - wordLayout.mCharactersLayoutInfo.size() );
734         paragraphLayout.mNumberOfCharacters -= removedNumberOfCharacters;
735       }
736       UpdateLayoutInfo( paragraphLayout, layoutParameters.mLineHeightOffset );
737
738       // Insert the text-actors in order.
739       removedTextActorsFromEnd.insert( removedTextActorsFromEnd.end(), removedTextActorsFromFirstWord.begin(), removedTextActorsFromFirstWord.end() );
740       removedTextActorsFromEnd.insert( removedTextActorsFromEnd.end(), removedTextActorsFromFirstParagraph.begin(), removedTextActorsFromFirstParagraph.end() );
741     }
742   } // end delete text from different paragraphs
743   else if( ( textInfoIndicesBegin.mParagraphIndex == textInfoIndicesEnd.mParagraphIndex ) && ( paragraphLayout.mNumberOfCharacters == numberOfCharacters ) )
744   {
745     // the whole paragraph needs to be deleted.
746     ++textInfoIndicesEnd.mParagraphIndex; // will delete current paragraph.
747   }
748   else
749   {
750     // deleted text is within the same paragraph. (merge paragraphs could be needed if the paragraph separator character is deleted)
751
752     // Paragraph which contains the characters to be deleted.
753     ParagraphLayoutInfo& paragraphLayout( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndicesBegin.mParagraphIndex ) );
754
755     // Remove the characters from the text and the styles.
756     const std::size_t firstCharacterIndex = textInfoIndicesBegin.mCharacterParagraphIndex;
757     const std::size_t lastCharacterIndex = textInfoIndicesEnd.mCharacterParagraphIndex + 1u;
758
759     paragraphLayout.mText.Remove( firstCharacterIndex, lastCharacterIndex - firstCharacterIndex );
760
761     for( Vector<TextStyle*>::Iterator it = paragraphLayout.mTextStyles.Begin() + firstCharacterIndex,
762            endIt = paragraphLayout.mTextStyles.Begin() + lastCharacterIndex;
763          it != endIt;
764          ++it )
765     {
766       delete *it;
767     }
768     paragraphLayout.mTextStyles.Erase( paragraphLayout.mTextStyles.Begin() + firstCharacterIndex,
769                                        paragraphLayout.mTextStyles.Begin() + lastCharacterIndex );
770
771     // Remove the characters from the paragraph layout info. It returns whether the current paragraph can be merged with the next one.
772     RemoveCharactersFromParagraphInfo( relayoutData,
773                                        numberOfCharacters,
774                                        mergeWords,
775                                        mergeParagraphs,
776                                        textInfoIndicesBegin,
777                                        textInfoIndicesEnd,
778                                        textInfoMergeIndicesBegin,
779                                        textInfoMergeIndicesEnd,
780                                        paragraphLayout,
781                                        removedTextActorsFromBegin,
782                                        removedTextActorsFromEnd );
783
784     if( mergeWords )
785     {
786       // Merges words pointed by textInfoMergeIndicesBegin.mWordIndex and textInfoMergeIndicesEnd.mWordIndex calculated previously.
787       DALI_ASSERT_DEBUG( ( textInfoMergeIndicesBegin.mWordIndex < paragraphLayout.mWordsLayoutInfo.size() ) && "TextViewProcessor::UpdateTextInfo (delete). Word index (begin) out of bounds." );
788       DALI_ASSERT_DEBUG( ( textInfoMergeIndicesEnd.mWordIndex < paragraphLayout.mWordsLayoutInfo.size() ) && "TextViewProcessor::UpdateTextInfo (delete). Word index (end) out of bounds." );
789
790       WordLayoutInfo& firstWordLayout( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoMergeIndicesBegin.mWordIndex ) );
791       WordLayoutInfo& lastWordLayout( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoMergeIndicesEnd.mWordIndex ) );
792
793       MergeWord( firstWordLayout,
794                  lastWordLayout );
795     }
796
797     // Store text-actors before removing them.
798     const std::size_t endIndex = ( mergeWords && ( textInfoIndicesEnd.mWordIndex > 0u ) ) ? textInfoIndicesEnd.mWordIndex - 1u : textInfoIndicesEnd.mWordIndex; // text-actors from the last word may have been added in the merge above.
799     CollectTextActorsFromWords( removedTextActorsFromMid, paragraphLayout, textInfoIndicesBegin.mWordIndex, endIndex );
800
801     // Remove unwanted words using previously calculated indices. (including the last part of the merged word)
802     paragraphLayout.mWordsLayoutInfo.erase( paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex, paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex );
803
804     // Update paragraph info.
805     UpdateLayoutInfo( paragraphLayout, layoutParameters.mLineHeightOffset );
806   }// end delete text from same paragraph.
807
808   if( mergeParagraphs )
809   {
810     // Merges paragraphs pointed by textInfoMergeIndicesBegin.mParagraphIndex and textInfoMergeIndicesEnd.mParagraphIndex calculated previously.
811
812     ParagraphLayoutInfo& firstParagraphLayout( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoMergeIndicesBegin.mParagraphIndex ) );
813
814     const ParagraphLayoutInfo& lastParagraphLayout( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoMergeIndicesEnd.mParagraphIndex ) );
815
816     MergeParagraph( firstParagraphLayout,
817                lastParagraphLayout );
818   }
819
820   // Store text-actors before removing them.
821   const std::size_t endIndex = ( mergeParagraphs && ( textInfoIndicesEnd.mParagraphIndex > 0u ) ) ? textInfoIndicesEnd.mParagraphIndex - 1u : textInfoIndicesEnd.mParagraphIndex; // text-actors from the last paragraph may have been added in the merge above.
822   CollectTextActorsFromParagraphs( removedTextActorsFromMid,
823                               relayoutData.mTextLayoutInfo,
824                               textInfoIndicesBegin.mParagraphIndex,
825                               endIndex );
826
827   // Remove unwanted paragraphs using previously calculated indices. (including the last part of the merged paragraph)
828   relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.erase( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndicesBegin.mParagraphIndex,
829                                                        relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndicesEnd.mParagraphIndex );
830
831   // Update text info.
832   UpdateLayoutInfo( relayoutData.mTextLayoutInfo );
833
834   // If the last character of the last paragraph is a new paragraph character, an empty paragraph need to be added.
835   if( !relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.empty() )
836   {
837     const WordLayoutInfo lastWordLayout = GetLastWordLayoutInfo( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.end() - 1u ) );
838
839     if( ParagraphSeparator == lastWordLayout.mType )
840     {
841       ParagraphLayoutInfo lastParagraphLayout;
842
843       const CharacterLayoutInfo layoutInfo = GetLastCharacterLayoutInfo( lastWordLayout );
844       lastParagraphLayout.mSize.height = layoutInfo.mSize.height;
845
846       relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.push_back( lastParagraphLayout );
847
848       relayoutData.mTextLayoutInfo.mWholeTextSize.height += layoutInfo.mSize.height;
849     }
850   }
851
852   // Clear the text from the text-actors if required.
853   if( CLEAR_TEXT == clearText )
854   {
855     ClearText( removedTextActorsFromEnd );
856     ClearText( removedTextActorsFromMid );
857     ClearText( removedTextActorsFromBegin );
858   }
859
860   // Insert text-actors into the cache.
861   // Text-actors are inserted in reverse order to use first the first removed.
862   relayoutData.mTextActorCache.InsertTextActors( removedTextActorsFromEnd );
863   relayoutData.mTextActorCache.InsertTextActors( removedTextActorsFromMid );
864   relayoutData.mTextActorCache.InsertTextActors( removedTextActorsFromBegin );
865 }
866
867 void UpdateTextInfo( std::size_t position,
868                      std::size_t numberOfCharacters,
869                      const MarkupProcessor::StyledTextArray& text,
870                      const TextView::LayoutParameters& layoutParameters,
871                      TextView::RelayoutData& relayoutData )
872 {
873   // Replaces 'numberOfCharacters' of text starting from 'position' with the given text.
874
875   // TODO: Temporary implementation with remove and insert.
876
877   // Remove.
878   UpdateTextInfo( position,
879                   numberOfCharacters,
880                   layoutParameters,
881                   relayoutData,
882                   KEEP_TEXT ); // Do not clear the text from the text-actors.
883
884   // Insert.
885   UpdateTextInfo( position,
886                   text,
887                   layoutParameters,
888                   relayoutData );
889 }
890
891 void UpdateTextInfo( float lineHeightOffset,
892                      TextLayoutInfo& textLayoutInfo )
893 {
894   // Updates the space between lines with the new offset value.
895
896   float newTextHeight = 0.f;
897
898   for( ParagraphLayoutInfoContainer::iterator paragraphIt = textLayoutInfo.mParagraphsLayoutInfo.begin(), paragraphEndIt = textLayoutInfo.mParagraphsLayoutInfo.end();
899        paragraphIt != paragraphEndIt;
900        ++paragraphIt )
901   {
902     ParagraphLayoutInfo& paragraphLayoutInfo( *paragraphIt );
903
904     paragraphLayoutInfo.mSize.height += ( lineHeightOffset - paragraphLayoutInfo.mLineHeightOffset );
905     newTextHeight += paragraphLayoutInfo.mSize.height;
906
907     paragraphLayoutInfo.mLineHeightOffset = lineHeightOffset;
908   }
909
910   textLayoutInfo.mWholeTextSize.height = newTextHeight;
911 }
912
913 void UpdateTextInfo( const TextStyle& style,
914                      const TextStyle::Mask mask,
915                      TextView::RelayoutData& relayoutData )
916 {
917   // Change text style for all text-actors.
918
919   for( ParagraphLayoutInfoContainer::iterator paragraphIt = relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin(), paragraphEndIt = relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.end();
920        paragraphIt != paragraphEndIt;
921        ++paragraphIt )
922   {
923     ParagraphLayoutInfo& paragraph( *paragraphIt );
924
925     std::size_t index = 0u;
926     for( Vector<TextStyle*>::Iterator it = paragraph.mTextStyles.Begin(), endIt = paragraph.mTextStyles.End(); it != endIt; ++it )
927     {
928       (*it)->Copy( style, mask );
929
930       // Checks if the font family supports all glyphs. If not, chooses a most suitable one.
931       ChooseFontFamilyName( paragraph.mText[index], *(*it) );
932
933       ++index;
934     }
935
936     for( WordLayoutInfoContainer::iterator wordIt = paragraph.mWordsLayoutInfo.begin(), wordEndIt = paragraph.mWordsLayoutInfo.end();
937          wordIt != wordEndIt;
938          ++wordIt )
939     {
940       WordLayoutInfo& word( *wordIt );
941
942       for( CharacterLayoutInfoContainer::iterator characterIt = word.mCharactersLayoutInfo.begin(), characterEndIt = word.mCharactersLayoutInfo.end();
943            characterIt != characterEndIt;
944            ++characterIt )
945       {
946         CharacterLayoutInfo& characterLayout( *characterIt );
947
948         // Mark the character to be set the new style into the text-actor.
949         characterLayout.mSetStyle = true;
950       } // end characters
951     } // end words
952   } // end paragraphs
953 }
954
955 } // namespace TextViewProcessor
956
957 } // namespace Internal
958
959 } // namespace Toolkit
960
961 } // namespace Dali