DALi Version 1.0.33
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / text-view / text-view-paragraph-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-paragraph-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-processor-helper-functions.h>
24 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
25 #include <dali-toolkit/internal/controls/text-view/text-processor-bidirectional-info.h>
26
27 namespace Dali
28 {
29
30 namespace Toolkit
31 {
32
33 namespace Internal
34 {
35
36 namespace TextViewProcessor
37 {
38
39 /////////////////////
40 // Layout info.
41 /////////////////////
42
43 void RightToLeftParagraphLayout::Clear()
44 {
45   mWordsLayoutInfo.clear();
46   mText = Text();
47   mTextStyles.Clear();
48 }
49
50 ParagraphLayoutInfo::ParagraphLayoutInfo()
51 : mSize(),
52   mAscender( 0.f ),
53   mLineHeightOffset( 0.f ),
54   mFirstCharacter( 0u ),
55   mNumberOfCharacters( 0u ),
56   mWordsLayoutInfo(),
57   mText(),
58   mTextStyles(),
59   mRightToLeftLayout( NULL ),
60   mBidirectionalParagraphInfo( NULL ),
61   mBidirectionalLinesInfo()
62 {
63 }
64
65 ParagraphLayoutInfo::~ParagraphLayoutInfo()
66 {
67   if( NULL != mRightToLeftLayout )
68   {
69     // TextStyle pointers are the same than the ones stored at ParagraphLayoutInfo::mTextStyles.
70     // Do not delete them, just clear the vector.
71     mRightToLeftLayout->mTextStyles.Clear();
72
73     delete mRightToLeftLayout;
74   }
75
76   // Clears text styles. It destroys TextStyle objects.
77   ClearStyles();
78
79   // Deletes the bidirectional info for the whole paragraph.
80   delete mBidirectionalParagraphInfo;
81
82   // Clears the bidirectional info for all lines. Destroys the BidirectionalLineInfo objects.
83   ClearBidirectionalInfo();
84 }
85
86 ParagraphLayoutInfo::ParagraphLayoutInfo( const ParagraphLayoutInfo& paragraph )
87 : mSize( paragraph.mSize ),
88   mAscender( paragraph.mAscender ),
89   mLineHeightOffset( paragraph.mLineHeightOffset ),
90   mFirstCharacter( paragraph.mFirstCharacter ),
91   mNumberOfCharacters( paragraph.mNumberOfCharacters ),
92   mWordsLayoutInfo( paragraph.mWordsLayoutInfo ),
93   mText( paragraph.mText ),
94   mTextStyles(),
95   mRightToLeftLayout( NULL ),
96   // Copies bidirectional info for the whole paragraph.
97   mBidirectionalParagraphInfo( ( NULL == paragraph.mBidirectionalParagraphInfo ) ? NULL : new TextProcessor::BidirectionalParagraphInfo( *paragraph.mBidirectionalParagraphInfo ) ),
98   mBidirectionalLinesInfo()
99 {
100   // Copies styles.
101   for( Vector<TextStyle*>::ConstIterator it = paragraph.mTextStyles.Begin(), endIt = paragraph.mTextStyles.End(); it != endIt; ++it )
102   {
103     mTextStyles.PushBack( new TextStyle( *(*it) ) );
104   }
105
106   // Copies bidirectional info for each line.
107   for( Vector<TextProcessor::BidirectionalLineInfo*>::ConstIterator it = mBidirectionalLinesInfo.Begin(), endIt = mBidirectionalLinesInfo.End(); it != endIt; ++it )
108   {
109     mBidirectionalLinesInfo.PushBack( new TextProcessor::BidirectionalLineInfo( *( *it ) ) );
110   }
111 }
112
113 ParagraphLayoutInfo& ParagraphLayoutInfo::operator=( const ParagraphLayoutInfo& paragraph )
114 {
115   mSize = paragraph.mSize;
116   mAscender = paragraph.mAscender;
117   mLineHeightOffset = paragraph.mLineHeightOffset;
118   mFirstCharacter = paragraph.mFirstCharacter;
119   mNumberOfCharacters = paragraph.mNumberOfCharacters;
120   mWordsLayoutInfo = paragraph.mWordsLayoutInfo;
121   mText = paragraph.mText;
122
123   // If it has styles, destroy them.
124   ClearStyles();
125
126   // Copies styles.
127   for( Vector<TextStyle*>::ConstIterator it = paragraph.mTextStyles.Begin(), endIt = paragraph.mTextStyles.End(); it != endIt; ++it )
128   {
129     mTextStyles.PushBack( new TextStyle( *(*it) ) );
130   }
131
132   // Copies the paragraph's bidirectiona info.
133   if( NULL == paragraph.mBidirectionalParagraphInfo )
134   {
135     // The source doesn't have bidirectiona info. Deletes the current one.
136     delete mBidirectionalParagraphInfo;
137     mBidirectionalParagraphInfo = NULL;
138   }
139   else
140   {
141     // The source has bidirectional info.
142     if( NULL != mBidirectionalParagraphInfo )
143     {
144       // It it has, copy to it.
145       *mBidirectionalParagraphInfo = *paragraph.mBidirectionalParagraphInfo;
146     }
147     else
148     {
149       // If it doesn't have, create a new one.
150       mBidirectionalParagraphInfo = new TextProcessor::BidirectionalParagraphInfo( *paragraph.mBidirectionalParagraphInfo );
151     }
152   }
153
154   // If it has bidirectiona info per line, destroy them.
155   ClearBidirectionalInfo();
156
157   // Copies bidirectional info per line.
158   for( Vector<TextProcessor::BidirectionalLineInfo*>::ConstIterator it = mBidirectionalLinesInfo.Begin(), endIt = mBidirectionalLinesInfo.End(); it != endIt; ++it )
159   {
160     mBidirectionalLinesInfo.PushBack( new TextProcessor::BidirectionalLineInfo( *( *it ) ) );
161   }
162
163   return *this;
164 }
165
166 void ParagraphLayoutInfo::ClearBidirectionalInfo()
167 {
168   // Destroys the bidirectional infor per line.
169   for( Vector<TextProcessor::BidirectionalLineInfo*>::Iterator it = mBidirectionalLinesInfo.Begin(), endIt = mBidirectionalLinesInfo.End(); it != endIt; ++it )
170   {
171     delete *it;
172   }
173   mBidirectionalLinesInfo.Clear();
174 }
175
176 void ParagraphLayoutInfo::ClearStyles()
177 {
178   // Destroys the styles.
179   for( Vector<TextStyle*>::Iterator it = mTextStyles.Begin(), endIt = mTextStyles.End(); it != endIt; ++it )
180   {
181     delete *it;
182   }
183   mTextStyles.Clear();
184 }
185
186 void CreateParagraphInfo( TextView::RelayoutData& relayoutData,
187                           ParagraphLayoutInfo& paragraphLayoutInfo )
188 {
189   if( TextProcessor::ContainsRightToLeftCharacter( paragraphLayoutInfo.mText ) )
190   {
191     // If the text is bidirectional, the characters will be converted and reordered
192     // as specified by the Unicode Bidirectional Algorithm.
193
194     paragraphLayoutInfo.mBidirectionalParagraphInfo = new TextProcessor::BidirectionalParagraphInfo();
195
196     TextProcessor::ProcessBidirectionalText( paragraphLayoutInfo.mText, paragraphLayoutInfo.mBidirectionalParagraphInfo );
197   }
198
199   // Split the paragraph in words. It retrieves the positions of white spaces and the last '\n' if there is one.
200   Vector<std::size_t> positions;
201   TextProcessor::SplitInWords( paragraphLayoutInfo.mText, positions );
202
203   const std::size_t lastCharacterIndex = paragraphLayoutInfo.mText.GetLength() - 1u;
204   const bool isLastCharacterParagraphSeparator = paragraphLayoutInfo.mText.IsNewLine( lastCharacterIndex );
205
206   // The number of words is ~the number of white spaces found + 1u.
207   // White spaces are also words.
208   // New line characters are also white spaces. If the last character is a white space the +1 is not needed.
209   const std::size_t numberOfWords = 2u * positions.Count() + ( isLastCharacterParagraphSeparator ? 0u : 1u );
210
211   // Reserve space for all the words.
212   paragraphLayoutInfo.mWordsLayoutInfo.resize( numberOfWords, WordLayoutInfo() );
213
214   // Traverses all positions creating and setting all character layout info objects to every word.
215   std::size_t wordIndex = 0u;
216   Vector<std::size_t>::ConstIterator positionIt = positions.Begin();
217   Vector<std::size_t>::ConstIterator positionEndIt = positions.End();
218   std::size_t from = 0u;
219   for( std::size_t positionIndex = 0u, size = positions.Count() + 1u; positionIndex < size; ++positionIndex )
220   {
221     const bool isEndPosition = positionIt == positionEndIt;
222     const std::size_t to = isEndPosition ? lastCharacterIndex + 1u : *positionIt;
223
224     if( from < to )
225     {
226       // The word is not a white space.
227       WordLayoutInfo& wordLayoutInfo = *( paragraphLayoutInfo.mWordsLayoutInfo.begin() + wordIndex );
228       ++wordIndex;
229       // Sets the index within the paragraph to the first character of the word.
230       wordLayoutInfo.mFirstCharacter = from;
231       // Creates character layout info objects.
232       wordLayoutInfo.mCharactersLayoutInfo.resize( ( to - from ), CharacterLayoutInfo() );
233     }
234
235     if( !isEndPosition )
236     {
237       // Create a word for the white space.
238       WordLayoutInfo& wordLayoutInfo = *( paragraphLayoutInfo.mWordsLayoutInfo.begin() + wordIndex );
239       ++wordIndex;
240       // Sets the index within the paragraph to the white space.
241       wordLayoutInfo.mFirstCharacter = to;
242       wordLayoutInfo.mType = WordSeparator;
243
244       CharacterLayoutInfo characterLayoutInfo;
245       wordLayoutInfo.mCharactersLayoutInfo.push_back( characterLayoutInfo );
246     }
247
248     from = to + 1u;
249
250     if( !isEndPosition )
251     {
252       // next white space position.
253       ++positionIt;
254     }
255     else
256     {
257       // All white space positiona have been traversed.
258       // It may be some extra words. i.e if the text is \n.
259       // Erase them.
260       paragraphLayoutInfo.mWordsLayoutInfo.erase( paragraphLayoutInfo.mWordsLayoutInfo.begin() + wordIndex, paragraphLayoutInfo.mWordsLayoutInfo.end() );
261
262       // Check if the last character is a new paragraph character.
263       if( isLastCharacterParagraphSeparator )
264       {
265         ( *( paragraphLayoutInfo.mWordsLayoutInfo.end() - 1u ) ).mType = ParagraphSeparator;
266       }
267     }
268   }
269
270   // Traverse all words and fill the layout info.
271   for( WordLayoutInfoContainer::iterator it = paragraphLayoutInfo.mWordsLayoutInfo.begin(), endIt = paragraphLayoutInfo.mWordsLayoutInfo.end(); it != endIt; ++it )
272   {
273     WordLayoutInfo& wordLayoutInfo( *it );
274
275     CreateWordTextInfo( paragraphLayoutInfo.mText,
276                         paragraphLayoutInfo.mTextStyles,
277                         wordLayoutInfo );
278
279     // Update layout info for the current paragraph.
280     UpdateSize( paragraphLayoutInfo.mSize, wordLayoutInfo.mSize );
281     paragraphLayoutInfo.mAscender = std::max( paragraphLayoutInfo.mAscender, wordLayoutInfo.mAscender );
282     paragraphLayoutInfo.mNumberOfCharacters += wordLayoutInfo.mCharactersLayoutInfo.size();
283
284     // Update the max word width figure.
285     relayoutData.mTextLayoutInfo.mMaxWordWidth = std::max( relayoutData.mTextLayoutInfo.mMaxWordWidth, wordLayoutInfo.mSize.width );
286   } // end of words
287 }
288
289 void UpdateLayoutInfo( ParagraphLayoutInfo& paragraphLayoutInfo, float lineHeightOffset )
290 {
291   // Update layout info.
292
293   // Initialize members to be updated.
294   paragraphLayoutInfo.mSize = Size::ZERO;
295   paragraphLayoutInfo.mAscender = 0.f;
296   paragraphLayoutInfo.mNumberOfCharacters = 0u;
297
298   // Traverses all words.
299   for( WordLayoutInfoContainer::iterator it = paragraphLayoutInfo.mWordsLayoutInfo.begin(), endIt = paragraphLayoutInfo.mWordsLayoutInfo.end();
300        it != endIt;
301        ++it )
302   {
303     WordLayoutInfo& word( *it );
304
305     // Sets the index within the paragraph to the first character of the word.
306     word.mFirstCharacter = paragraphLayoutInfo.mNumberOfCharacters;
307
308     // Updates the paragraph's size.
309     UpdateSize( paragraphLayoutInfo.mSize, word.mSize );
310
311     // Updates the paragraph's max asender.
312     paragraphLayoutInfo.mAscender = std::max( paragraphLayoutInfo.mAscender, word.mAscender );
313
314     // Updates the paragraph's number of characters.
315     paragraphLayoutInfo.mNumberOfCharacters += word.mCharactersLayoutInfo.size();
316   }
317
318   // Sets the line height offset.
319   paragraphLayoutInfo.mSize.height += lineHeightOffset;
320   paragraphLayoutInfo.mLineHeightOffset = lineHeightOffset;
321 }
322
323 void RemoveWordsFromParagraph( std::size_t wordIndex,
324                                std::size_t numberOfWords,
325                                float lineHeightOffset,
326                                ParagraphLayoutInfo& paragraphLayout )
327 {
328   // Removes words from a paragraph.
329
330   // * Check if words or paragraphs can be merged after removing a number of words or a paragraph separator needs to be done outside this method.
331
332   // Remove words from layout info.
333   paragraphLayout.mWordsLayoutInfo.erase( paragraphLayout.mWordsLayoutInfo.begin() + wordIndex,
334                                      paragraphLayout.mWordsLayoutInfo.begin() + ( wordIndex + numberOfWords ) );
335
336   UpdateLayoutInfo( paragraphLayout, lineHeightOffset );
337 }
338
339 void RemoveCharactersFromParagraphInfo( TextView::RelayoutData& relayoutData,
340                                    const std::size_t numberOfCharacters,
341                                    bool& mergeWords,
342                                    bool& mergeParagraphs,
343                                    TextInfoIndices& textInfoIndicesBegin,
344                                    TextInfoIndices& textInfoIndicesEnd,
345                                    TextInfoIndices& textInfoMergeIndicesBegin,
346                                    TextInfoIndices& textInfoMergeIndicesEnd,
347                                    ParagraphLayoutInfo& paragraphLayout,
348                                    std::vector<TextActor>& removedTextActorsFromFirstWord,
349                                    std::vector<TextActor>& removedTextActorsFromLastWord )
350 {
351   const TextLayoutInfo& textLayoutInfo = relayoutData.mTextLayoutInfo;
352
353   if( textInfoIndicesBegin.mWordIndex < textInfoIndicesEnd.mWordIndex )
354   {
355     // Deleted text is from different words. The two different words may be merged.
356
357     // Get first word.
358     WordLayoutInfo& firstWordLayout( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex ) );
359
360     // Get last word.
361     WordLayoutInfo& lastWordLayout( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex ) );
362
363     // whether first or last word need to be split and merged.
364     bool mergeFromBegin = false;
365     bool mergeToEnd = false;
366
367     if( textInfoIndicesBegin.mCharacterIndex > 0u )
368     {
369       // First word is going to be split. It could be merged with the last word.
370       mergeFromBegin = true;
371       textInfoMergeIndicesBegin.mWordIndex = textInfoIndicesBegin.mWordIndex;
372     }
373     else if( ( textInfoIndicesBegin.mCharacterIndex == 0u ) && ( textInfoIndicesBegin.mWordIndex > 0u ) )
374     {
375       // First word is going to be removed completely.
376       // Check if previous word could be merged.
377
378       // Get word before.
379       WordLayoutInfo& previousWordLayout( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex - 1u ) );
380       if( WordSeparator != previousWordLayout.mType )
381       {
382         // Previous word is not a word separator, so could be merged.
383         mergeFromBegin = true;
384         textInfoMergeIndicesBegin.mWordIndex = textInfoIndicesBegin.mWordIndex - 1u;
385       }
386     }
387
388     if( mergeFromBegin )
389     {
390       // First word (or previous one) could be merged. Check if last one could be merged as well.
391
392       if( textInfoIndicesEnd.mCharacterIndex + 1u < lastWordLayout.mCharactersLayoutInfo.size() )
393       {
394         // Last word is going to be split. It could be merged with the first word.
395         mergeToEnd = true;
396         textInfoMergeIndicesEnd.mWordIndex = textInfoIndicesEnd.mWordIndex;
397       }
398       else if( ( textInfoIndicesEnd.mCharacterIndex + 1u == lastWordLayout.mCharactersLayoutInfo.size() ) && ( textInfoIndicesEnd.mWordIndex + 1u < paragraphLayout.mWordsLayoutInfo.size() ) )
399       {
400         // Last word is going to be removed completely.
401         // Check if the word after could be merged.
402
403         // Get word after.
404         WordLayoutInfo& afterWordLayout( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex + 1u ) );
405         if( WordSeparator != afterWordLayout.mType )
406         {
407           // The word after is not a word separator, so could be merged.
408           mergeToEnd = true;
409           textInfoMergeIndicesEnd.mWordIndex = textInfoIndicesEnd.mWordIndex + 1u;
410         }
411       }
412
413       // Merge words only if both words could be merged.
414       mergeWords = mergeFromBegin && mergeToEnd;
415     }
416
417     if( ( textInfoIndicesEnd.mCharacterIndex + 1u == lastWordLayout.mCharactersLayoutInfo.size() ) && ( textInfoIndicesEnd.mWordIndex + 1u == paragraphLayout.mWordsLayoutInfo.size() ) )
418     {
419       // Last word of the paragraph is going to be removed completely.
420       // Check if it's a paragraph separator.
421
422       if( ParagraphSeparator == lastWordLayout.mType )
423       {
424         // The paragraph separator is going to be removed.
425         if( textInfoIndicesBegin.mParagraphIndex + 1u < textLayoutInfo.mParagraphsLayoutInfo.size() )
426         {
427           //  Paragraph need to be merged.
428           textInfoMergeIndicesBegin.mParagraphIndex = textInfoIndicesBegin.mParagraphIndex;
429           textInfoMergeIndicesEnd.mParagraphIndex = textInfoIndicesBegin.mParagraphIndex + 1u;
430           mergeParagraphs= true;
431
432           ++textInfoIndicesBegin.mParagraphIndex; // increase both indices,
433           textInfoIndicesEnd.mParagraphIndex +=2u; // will delete last paragraph.
434         }
435       }
436     }
437
438     if( textInfoIndicesBegin.mCharacterIndex > 0u )
439     {
440       // First word needs to be split.
441
442       // Store text-actors before removing them.
443       CollectTextActors( removedTextActorsFromFirstWord, firstWordLayout, textInfoIndicesBegin.mCharacterIndex, firstWordLayout.mCharactersLayoutInfo.size() );
444
445       RemoveCharactersFromWord( textInfoIndicesBegin.mCharacterIndex,
446                                 firstWordLayout.mCharactersLayoutInfo.size() - textInfoIndicesBegin.mCharacterIndex,
447                                 firstWordLayout );
448
449       ++textInfoIndicesBegin.mWordIndex; // will delete from the word after.
450     }
451
452     if( textInfoIndicesEnd.mCharacterIndex + 1u < lastWordLayout.mCharactersLayoutInfo.size() )
453     {
454       // Last word needs to be split.
455
456       // Store text-actors before removing them.
457       CollectTextActors( removedTextActorsFromLastWord, lastWordLayout, 0u, textInfoIndicesEnd.mCharacterIndex + 1u );
458
459       RemoveCharactersFromWord( 0u,
460                                 textInfoIndicesEnd.mCharacterIndex + 1u,
461                                 lastWordLayout );
462
463       if( mergeWords )
464       {
465         // This word is going to be merged, so is not needed.
466         ++textInfoIndicesEnd.mWordIndex; // will delete the last word.
467       }
468     }
469     else if( textInfoIndicesEnd.mCharacterIndex + 1u == lastWordLayout.mCharactersLayoutInfo.size() )
470     {
471       // The whole last word is going to be removed.
472       ++textInfoIndicesEnd.mWordIndex; // will delete the last word.
473
474       if( ( WordSeparator == lastWordLayout.mType ) && mergeWords )
475       {
476         // The last word is a word separator and the word after is going to be merged so is not needed.
477         ++textInfoIndicesEnd.mWordIndex; // will delete the word after the last one.
478       }
479     }
480   }
481   else
482   {
483     // Chraracters to be removed are from the same word.
484
485     RemoveCharactersFromWordInfo( relayoutData,
486                                   numberOfCharacters,
487                                   mergeWords,
488                                   mergeParagraphs,
489                                   textInfoIndicesBegin,
490                                   textInfoIndicesEnd,
491                                   textInfoMergeIndicesBegin,
492                                   textInfoMergeIndicesEnd,
493                                   paragraphLayout,
494                                   removedTextActorsFromFirstWord );
495   } // word indices
496 }
497
498 void SplitParagraph( const TextInfoIndices& indices,
499                      float lineHeightOffset,
500                      ParagraphLayoutInfo& firstParagraphLayoutInfo,
501                      ParagraphLayoutInfo& lastParagraphLayoutInfo )
502 {
503   // Splits a paragraph in two.
504   // A word may be split in two as well.
505
506   // * Split the word within the paragraph.
507   // * Add last part of the word to the new paragraph.
508   // * Add words from wordPosition + 1 to the end.
509   // * Update layout info of the last paragraph.
510   // * Remove words added to the last part of the paragraph from the first paragraph.
511
512   // early returns!!
513   if( ( 0u == indices.mWordIndex ) && ( 0u == indices.mCharacterIndex ) )
514   {
515     // the whole paragraph goes to the last part.
516     lastParagraphLayoutInfo = firstParagraphLayoutInfo;
517
518     firstParagraphLayoutInfo = ParagraphLayoutInfo();
519
520     return;
521   }
522
523   if( !firstParagraphLayoutInfo.mWordsLayoutInfo.empty() )
524   {
525     const std::size_t numberOfWords = firstParagraphLayoutInfo.mWordsLayoutInfo.size();
526     if( indices.mWordIndex == numberOfWords - 1u )
527     {
528       const WordLayoutInfo& word( *( firstParagraphLayoutInfo.mWordsLayoutInfo.end() - 1u ) );
529       if( indices.mCharacterIndex == word.mCharactersLayoutInfo.size() )
530       {
531         // the whole paragraph goes to the first part.
532
533         // Just delete whatever there is in the last part of the paragraph.
534         lastParagraphLayoutInfo = ParagraphLayoutInfo();
535
536         return;
537       }
538     }
539   }
540
541   lastParagraphLayoutInfo = ParagraphLayoutInfo();
542
543   // 1) Split the word whitin the paragraph.
544   WordLayoutInfo& firstWordLayoutInfo( *( firstParagraphLayoutInfo.mWordsLayoutInfo.begin() + indices.mWordIndex ) );
545   WordLayoutInfo lastWordLayoutInfo;
546
547   SplitWord( indices.mCharacterIndex,
548              firstWordLayoutInfo,
549              lastWordLayoutInfo );
550
551   // 2) Add last part of the word to the new paragraph.
552   if( !lastWordLayoutInfo.mCharactersLayoutInfo.empty() )
553   {
554     lastParagraphLayoutInfo.mWordsLayoutInfo.push_back( lastWordLayoutInfo );
555   }
556
557   // 3) Add words from word-position + 1 to the end.
558   lastParagraphLayoutInfo.mWordsLayoutInfo.insert( lastParagraphLayoutInfo.mWordsLayoutInfo.end(),
559                                                    firstParagraphLayoutInfo.mWordsLayoutInfo.begin() + indices.mWordIndex + 1u,
560                                                    firstParagraphLayoutInfo.mWordsLayoutInfo.end() );
561
562   // 4) update layout info of the last paragraph.
563   for( WordLayoutInfoContainer::iterator it = lastParagraphLayoutInfo.mWordsLayoutInfo.begin(), endIt = lastParagraphLayoutInfo.mWordsLayoutInfo.end();
564        it != endIt;
565        ++it )
566   {
567     WordLayoutInfo& layoutInfo( *it );
568
569     UpdateSize( lastParagraphLayoutInfo.mSize, layoutInfo.mSize );
570     lastParagraphLayoutInfo.mNumberOfCharacters += layoutInfo.mCharactersLayoutInfo.size();
571     lastParagraphLayoutInfo.mAscender = std::max( lastParagraphLayoutInfo.mAscender, layoutInfo.mAscender );
572   }
573   lastParagraphLayoutInfo.mSize.height += lineHeightOffset;
574   lastParagraphLayoutInfo.mLineHeightOffset = lineHeightOffset;
575
576   // 5) Remove words added to the last part of the paragraph from the first paragraph.
577
578   // if the number of characters of the last word of the first paragraph is zero, it should be removed.
579   const std::size_t index = ( firstWordLayoutInfo.mCharactersLayoutInfo.empty() ? indices.mWordIndex : indices.mWordIndex + 1u );
580
581   firstParagraphLayoutInfo.mWordsLayoutInfo.erase( firstParagraphLayoutInfo.mWordsLayoutInfo.begin() + index, firstParagraphLayoutInfo.mWordsLayoutInfo.end() );
582
583   // 6) update layout info of the first paragraph.
584   UpdateLayoutInfo( firstParagraphLayoutInfo, lineHeightOffset );
585
586   // 7) Split text and styles.
587
588   // Copies the whole text to the last part of the paragraph.
589   lastParagraphLayoutInfo.mText = firstParagraphLayoutInfo.mText;
590
591   // Removes from the first part of the paragraph the text that goes to the last part.
592   firstParagraphLayoutInfo.mText.Remove( indices.mCharacterParagraphIndex, firstParagraphLayoutInfo.mText.GetLength() - indices.mCharacterParagraphIndex );
593
594   // Removes from the last part of the paragraph the text that remains in the first part.
595   lastParagraphLayoutInfo.mText.Remove( 0u, indices.mCharacterParagraphIndex );
596
597   // Sets the character's styles for the last part of the paragraph.
598   lastParagraphLayoutInfo.mTextStyles.Insert( lastParagraphLayoutInfo.mTextStyles.End(),
599                                          firstParagraphLayoutInfo.mTextStyles.Begin() + indices.mCharacterParagraphIndex,
600                                          firstParagraphLayoutInfo.mTextStyles.End() );
601
602   // Removes the character's styles that go to the last part of the paragraph.
603   firstParagraphLayoutInfo.mTextStyles.Erase( firstParagraphLayoutInfo.mTextStyles.Begin() + indices.mCharacterParagraphIndex,
604                                          firstParagraphLayoutInfo.mTextStyles.End() );
605 }
606
607 void MergeParagraph( ParagraphLayoutInfo& firstParagraphLayoutInfo,
608                      const ParagraphLayoutInfo& lastParagraphLayoutInfo )
609 {
610   // Merges two given paragraphs.
611   //
612   // Can't merge two paragraphs if the last word of the first one is a paragraph separator (new paragraph character)
613
614   // Early returns.
615
616   if( lastParagraphLayoutInfo.mWordsLayoutInfo.empty() )
617   {
618     // Nothing to merge if last paragraph is empty.
619     return;
620   }
621
622   if( firstParagraphLayoutInfo.mWordsLayoutInfo.empty() )
623   {
624     // If first paragraph is empty, just copy the last paragraph to the first one.
625     firstParagraphLayoutInfo = lastParagraphLayoutInfo;
626
627    return;
628   }
629
630   // Check the last word of the first paragraph doesn't finish with a new paragraph character.
631   WordLayoutInfo& lastWordLayout( *( firstParagraphLayoutInfo.mWordsLayoutInfo.end() - 1u ) );
632   if( ParagraphSeparator == lastWordLayout.mType )
633   {
634     DALI_ASSERT_ALWAYS( !"TextViewProcessor::MergeParagraph(). ERROR: A paragraph can't be merged to another paragraph which finishes with a new paragraph character." );
635   }
636
637   // If the las word of the first paragraph or the first word of the last paragraph is a white space, both paragraphs can be concatenated.
638   // Otherwise both words need to be merged first.
639   const WordLayoutInfo& firstWordLayout( *lastParagraphLayoutInfo.mWordsLayoutInfo.begin() );
640
641   std::size_t index = 0u;
642   if( ( WordSeparator != lastWordLayout.mType ) && ( WordSeparator != firstWordLayout.mType ) && ( ParagraphSeparator != firstWordLayout.mType ) )
643   {
644     // Last word of the first paragraph is not a word separator and first word of the last paragraph is not a word or paragraph separator.
645     // Words need to be merged.
646
647     MergeWord( lastWordLayout,
648                firstWordLayout );
649
650     // After merging two words, the rest of the words need to be added.
651     ++index; // By increasing this index the word already merged won't be added again.
652   }
653
654   // Merge layout info
655
656   // Insert the layout of the words.
657   const std::size_t numberOfWords = firstParagraphLayoutInfo.mWordsLayoutInfo.size();
658   firstParagraphLayoutInfo.mWordsLayoutInfo.insert( firstParagraphLayoutInfo.mWordsLayoutInfo.end(),
659                                                    lastParagraphLayoutInfo.mWordsLayoutInfo.begin() + index, lastParagraphLayoutInfo.mWordsLayoutInfo.end() );
660
661   // Increase the index of the first character of each inserted word.
662   for( WordLayoutInfoContainer::iterator it = firstParagraphLayoutInfo.mWordsLayoutInfo.begin() + numberOfWords, endIt = firstParagraphLayoutInfo.mWordsLayoutInfo.end(); it != endIt; ++it )
663   {
664     WordLayoutInfo& word( *it );
665     word.mFirstCharacter += firstParagraphLayoutInfo.mNumberOfCharacters;
666   }
667
668   // Update the size and other layout parameters.
669   UpdateSize( firstParagraphLayoutInfo.mSize, lastParagraphLayoutInfo.mSize );
670   firstParagraphLayoutInfo.mAscender = std::max( firstParagraphLayoutInfo.mAscender, lastParagraphLayoutInfo.mAscender );
671   firstParagraphLayoutInfo.mLineHeightOffset = std::max( firstParagraphLayoutInfo.mLineHeightOffset, lastParagraphLayoutInfo.mLineHeightOffset );
672   firstParagraphLayoutInfo.mNumberOfCharacters += lastParagraphLayoutInfo.mNumberOfCharacters;
673
674   // Merge text and styles.
675   firstParagraphLayoutInfo.mText.Append( lastParagraphLayoutInfo.mText );
676   for( Vector<TextStyle*>::ConstIterator it = lastParagraphLayoutInfo.mTextStyles.Begin(), endIt = lastParagraphLayoutInfo.mTextStyles.End(); it != endIt; ++it )
677   {
678     firstParagraphLayoutInfo.mTextStyles.PushBack( new TextStyle( *(*it) ) );
679   }
680 }
681
682 WordLayoutInfo GetLastWordLayoutInfo( const ParagraphLayoutInfo& paragraphLayoutInfo )
683 {
684   WordLayoutInfo layoutInfo;
685
686   if( !paragraphLayoutInfo.mWordsLayoutInfo.empty() )
687   {
688     layoutInfo = *( paragraphLayoutInfo.mWordsLayoutInfo.end() - 1u );
689   }
690
691   return layoutInfo;
692 }
693
694 CharacterLayoutInfo GetFirstCharacterLayoutInfo( const ParagraphLayoutInfo& paragraphLayoutInfo )
695 {
696   CharacterLayoutInfo layoutInfo;
697
698   if( !paragraphLayoutInfo.mWordsLayoutInfo.empty() )
699   {
700     const WordLayoutInfo& wordInfo( *paragraphLayoutInfo.mWordsLayoutInfo.begin() );
701
702     layoutInfo = GetFirstCharacterLayoutInfo( wordInfo );
703   }
704
705   return layoutInfo;
706 }
707
708 CharacterLayoutInfo GetLastCharacterLayoutInfo( const ParagraphLayoutInfo& paragraphLayoutInfo )
709 {
710   const WordLayoutInfo wordInfo = GetLastWordLayoutInfo( paragraphLayoutInfo );
711
712   return GetLastCharacterLayoutInfo( wordInfo );
713 }
714
715 void CollectTextActorsFromParagraphs( std::vector<TextActor>& textActors, const TextLayoutInfo& textLayoutInfo, const std::size_t paragraphIndexBegin, const std::size_t paragraphIndexEnd )
716 {
717   for( ParagraphLayoutInfoContainer::const_iterator paragraphIt = textLayoutInfo.mParagraphsLayoutInfo.begin() + paragraphIndexBegin, paragraphEndIt = textLayoutInfo.mParagraphsLayoutInfo.begin() + paragraphIndexEnd;
718        paragraphIt != paragraphEndIt;
719        ++paragraphIt )
720   {
721     const ParagraphLayoutInfo& paragraph( *paragraphIt );
722
723     CollectTextActorsFromWords( textActors, paragraph, 0u, paragraph.mWordsLayoutInfo.size() );
724   }
725 }
726
727 } //namespace TextViewProcessor
728
729 } //namespace Internal
730
731 } //namespace Toolkit
732
733 } //namespace Dali