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