Merge "(TextInput) Emits text modified signal when cut or paste performed" into tizen
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / text-view / text-view-word-group-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-word-group-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
26 namespace Dali
27 {
28
29 namespace Toolkit
30 {
31
32 namespace Internal
33 {
34
35 namespace TextViewProcessor
36 {
37
38 /////////////////////
39 // Layout info.
40 /////////////////////
41
42 WordGroupLayoutInfo::WordGroupLayoutInfo()
43 : mSize(),
44   mAscender( 0.f ),
45   mDirection( LTR ),
46   mWordsLayoutInfo(),
47   mNumberOfCharacters( 0 )
48 {
49 }
50
51 WordGroupLayoutInfo::WordGroupLayoutInfo( const WordGroupLayoutInfo& group )
52 : mSize( group.mSize ),
53   mAscender( group.mAscender ),
54   mDirection( group.mDirection ),
55   mWordsLayoutInfo( group.mWordsLayoutInfo ),
56   mNumberOfCharacters( group.mNumberOfCharacters )
57 {
58 }
59
60 WordGroupLayoutInfo& WordGroupLayoutInfo::operator=( const WordGroupLayoutInfo& group )
61 {
62   mSize = group.mSize;
63   mAscender = group.mAscender;
64   mDirection = group.mDirection;
65   mWordsLayoutInfo = group.mWordsLayoutInfo;
66   mNumberOfCharacters = group.mNumberOfCharacters;
67
68   return *this;
69 }
70
71 void UpdateGroupLayoutInfo( TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo )
72 {
73   wordGroupLayoutInfo.mSize = Size();
74
75   for( WordLayoutInfoContainer::iterator it = wordGroupLayoutInfo.mWordsLayoutInfo.begin(), endIt = wordGroupLayoutInfo.mWordsLayoutInfo.end();
76        it != endIt;
77        ++it )
78   {
79     WordLayoutInfo& layoutInfo( *it );
80
81     UpdateSize( wordGroupLayoutInfo.mSize, layoutInfo.mSize );
82   }
83 }
84
85 void CreateWordGroupInfo( const MarkupProcessor::StyledTextArray& wordGroup,
86                           TextViewProcessor::TextLayoutInfo& textLayoutInfo,
87                           TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo )
88 {
89   // Set the direction of the group.
90   wordGroupLayoutInfo.mDirection = ( TextProcessor::BeginsRightToLeftCharacter( wordGroup ) ? TextViewProcessor::RTL : TextViewProcessor::LTR );
91
92   // Split the group of words in words
93   std::vector<MarkupProcessor::StyledTextArray> words;
94   TextProcessor::SplitInWords( wordGroup, words );
95
96   // if last word has a new line separator, create a new word.
97   if( !words.empty() )
98   {
99     MarkupProcessor::StyledTextArray& word( *( words.end() - 1 ) );
100     if( word.size() > 1 )
101     {
102       // do nothing if the word has only one character.
103       MarkupProcessor::StyledText& styledText( *( word.end() - 1 ) );
104       if( !styledText.mText.IsEmpty() )
105       {
106         const std::size_t length = styledText.mText.GetLength();
107         if( styledText.mText[length-1].IsNewLine() )
108         {
109           // Last character of this word is a new line character.
110
111           // Remove line separator character from current word.
112           styledText.mText.Remove( length - 1, 1 );
113
114           // Create a new word with the line separator character.
115           MarkupProcessor::StyledText newLineText( Text( styledText.mText[length-1] ), styledText.mStyle );
116
117           MarkupProcessor::StyledTextArray newLineWord;
118           newLineWord.push_back( newLineText );
119
120           words.push_back( newLineWord );
121         }
122       }
123     }
124   }
125
126   // Reverse if right to left.
127   if( TextViewProcessor::RTL == wordGroupLayoutInfo.mDirection )
128   {
129     std::reverse( words.begin(), words.end() );
130   }
131
132   std::string lastCharacterFont; // Keeps the font used by the last character. It's used to set the font to a word separator.
133
134   // Traverse all words.
135   for( std::vector<MarkupProcessor::StyledTextArray>::const_iterator wordIt = words.begin(), wordEndIt = words.end(); wordIt != wordEndIt; ++wordIt )
136   {
137     const MarkupProcessor::StyledTextArray& word( *wordIt );
138
139     // Data structures for the new word.
140     WordLayoutInfo wordLayoutInfo;
141
142     CreateWordTextInfo( word,
143                         wordLayoutInfo );
144
145     // White space's size could be different depending on the type of font. It's important to use the same font than the previous character to
146     // avoid 'jumps' of characters when there is a switch between one text-actor per character and one text-actor per line and/or style.
147     if( WordSeparator == wordLayoutInfo.mType )
148     {
149       // If current word is a word separator (white space) then the font of the last character is set.
150       for( CharacterLayoutInfoContainer::iterator characterIt = wordLayoutInfo.mCharactersLayoutInfo.begin(), characterEndIt = wordLayoutInfo.mCharactersLayoutInfo.end();
151            characterIt != characterEndIt;
152            ++characterIt )
153       {
154         CharacterLayoutInfo& characterLayout( *characterIt );
155
156         characterLayout.mStyledText.mStyle.SetFontName( lastCharacterFont );
157       }
158     }
159     else
160     {
161       // kepps the font of the last character.
162       if( !wordLayoutInfo.mCharactersLayoutInfo.empty() )
163       {
164         lastCharacterFont = ( *( wordLayoutInfo.mCharactersLayoutInfo.end() - 1 ) ).mStyledText.mStyle.GetFontName();
165       }
166     }
167
168     // Update layout info for the current group of words.
169     wordGroupLayoutInfo.mNumberOfCharacters += wordLayoutInfo.mCharactersLayoutInfo.size();
170     UpdateSize( wordGroupLayoutInfo.mSize, wordLayoutInfo.mSize );
171     wordGroupLayoutInfo.mAscender = std::max( wordGroupLayoutInfo.mAscender, wordLayoutInfo.mAscender );
172
173     // Add current word to the group of words.
174     wordGroupLayoutInfo.mWordsLayoutInfo.push_back( wordLayoutInfo );
175
176     // Update the max word width figure.
177     textLayoutInfo.mMaxWordWidth = std::max( textLayoutInfo.mMaxWordWidth, wordLayoutInfo.mSize.width );
178   } // end of words
179 }
180
181 void RemoveCharactersFromWordGroupInfo( TextView::RelayoutData& relayoutData,
182                                         const std::size_t numberOfCharacters,
183                                         bool& mergeWords,
184                                         bool& mergeLines,
185                                         TextViewProcessor::TextInfoIndices& textInfoIndicesBegin,
186                                         TextViewProcessor::TextInfoIndices& textInfoIndicesEnd,
187                                         TextViewProcessor::TextInfoIndices& textInfoMergeIndicesBegin,
188                                         TextViewProcessor::TextInfoIndices& textInfoMergeIndicesEnd,
189                                         TextViewProcessor::WordGroupLayoutInfo& groupLayout,
190                                         std::vector<TextActor>& removedTextActorsFromFirstWord,
191                                         std::vector<TextActor>& removedTextActorsFromLastWord )
192 {
193   const TextViewProcessor::TextLayoutInfo& textLayoutInfo = relayoutData.mTextLayoutInfo;
194
195   if( textInfoIndicesBegin.mWordIndex < textInfoIndicesEnd.mWordIndex )
196   {
197     // Deleted text is from different words. The two different words may be merged.
198
199     // Get first word.
200     WordLayoutInfo& firstWordLayout( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex ) );
201
202     // Get last word.
203     WordLayoutInfo& lastWordLayout( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex ) );
204
205     // whether first or last word need to be split and merged.
206     bool mergeFromBegin = false;
207     bool mergeToEnd = false;
208
209     if( textInfoIndicesBegin.mCharacterIndex > 0 )
210     {
211       // First word is going to be split. It could be merged with the last word.
212       mergeFromBegin = true;
213       textInfoMergeIndicesBegin.mWordIndex = textInfoIndicesBegin.mWordIndex;
214     }
215     else if( ( textInfoIndicesBegin.mCharacterIndex == 0 ) && ( textInfoIndicesBegin.mWordIndex > 0 ) )
216     {
217       // First word is going to be removed completely.
218       // Check if previous word could be merged.
219
220       // Get word before.
221       WordLayoutInfo& previousWordLayout( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex - 1 ) );
222       if( WordSeparator != previousWordLayout.mType )
223       {
224         // Previous word is not a word separator, so could be merged.
225         mergeFromBegin = true;
226         textInfoMergeIndicesBegin.mWordIndex = textInfoIndicesBegin.mWordIndex - 1;
227       }
228     }
229
230     if( mergeFromBegin )
231     {
232       // First word (or previous one) could be merged. Check if last one could be merged as well.
233
234       if( textInfoIndicesEnd.mCharacterIndex + 1 < lastWordLayout.mCharactersLayoutInfo.size() )
235       {
236         // Last word is going to be split. It could be merged with the first word.
237         mergeToEnd = true;
238         textInfoMergeIndicesEnd.mWordIndex = textInfoIndicesEnd.mWordIndex;
239       }
240       else if( ( textInfoIndicesEnd.mCharacterIndex + 1 == lastWordLayout.mCharactersLayoutInfo.size() ) && ( textInfoIndicesEnd.mWordIndex + 1 < groupLayout.mWordsLayoutInfo.size() ) )
241       {
242         // Last word is going to be removed completely.
243         // Check if the word after could be merged.
244
245         // Get word after.
246         WordLayoutInfo& afterWordLayout( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex + 1 ) );
247         if( WordSeparator != afterWordLayout.mType )
248         {
249           // The word after is not a word separator, so could be merged.
250           mergeToEnd = true;
251           textInfoMergeIndicesEnd.mWordIndex = textInfoIndicesEnd.mWordIndex + 1;
252         }
253       }
254
255       // Merge words only if both words could be merged.
256       mergeWords = mergeFromBegin && mergeToEnd;
257     }
258
259     if( ( textInfoIndicesEnd.mCharacterIndex + 1 == lastWordLayout.mCharactersLayoutInfo.size() ) && ( textInfoIndicesEnd.mWordIndex + 1 == groupLayout.mWordsLayoutInfo.size() ) )
260     {
261       // Last word of the line is going to be removed completely.
262       // Check if it's a line separator.
263
264       if( LineSeparator == lastWordLayout.mType )
265       {
266         // The line separator is going to be removed.
267         if( textInfoIndicesBegin.mLineIndex + 1 < textLayoutInfo.mLinesLayoutInfo.size() )
268         {
269           //  Line need to be merged.
270           textInfoMergeIndicesBegin.mLineIndex = textInfoIndicesBegin.mLineIndex;
271           textInfoMergeIndicesEnd.mLineIndex = textInfoIndicesBegin.mLineIndex + 1;
272           mergeLines= true;
273
274           ++textInfoIndicesBegin.mLineIndex; // increase both indices,
275           textInfoIndicesEnd.mLineIndex +=2; // will delete last line.
276         }
277       }
278     }
279
280     if( textInfoIndicesBegin.mCharacterIndex > 0 )
281     {
282       // First word needs to be split.
283
284       // Store text-actors before removing them.
285       CollectTextActors( removedTextActorsFromFirstWord, firstWordLayout, textInfoIndicesBegin.mCharacterIndex, firstWordLayout.mCharactersLayoutInfo.size() );
286
287       RemoveCharactersFromWord( textInfoIndicesBegin.mCharacterIndex,
288                                 firstWordLayout.mCharactersLayoutInfo.size() - textInfoIndicesBegin.mCharacterIndex,
289                                 firstWordLayout );
290
291       ++textInfoIndicesBegin.mWordIndex; // will delete from the word after.
292     }
293
294     if( textInfoIndicesEnd.mCharacterIndex + 1 < lastWordLayout.mCharactersLayoutInfo.size() )
295     {
296       // Last word needs to be split.
297
298       // Store text-actors before removing them.
299       CollectTextActors( removedTextActorsFromLastWord, lastWordLayout, 0, textInfoIndicesEnd.mCharacterIndex + 1 );
300
301       RemoveCharactersFromWord( 0,
302                                 textInfoIndicesEnd.mCharacterIndex + 1,
303                                 lastWordLayout );
304
305       if( mergeWords )
306       {
307         // This word is going to be merged, so is not needed.
308         ++textInfoIndicesEnd.mWordIndex; // will delete the last word.
309       }
310     }
311     else if( textInfoIndicesEnd.mCharacterIndex + 1 == lastWordLayout.mCharactersLayoutInfo.size() )
312     {
313       // The whole last word is going to be removed.
314       ++textInfoIndicesEnd.mWordIndex; // will delete the last word.
315
316       if( ( WordSeparator == lastWordLayout.mType ) && mergeWords )
317       {
318         // The last word is a word separator and the word after is going to be merged so is not needed.
319         ++textInfoIndicesEnd.mWordIndex; // will delete the word after the last one.
320       }
321     }
322   }
323   else
324   {
325     // Chraracters to be removed are from the same word.
326
327     RemoveCharactersFromWordInfo( relayoutData,
328                                   numberOfCharacters,
329                                   mergeWords,
330                                   mergeLines,
331                                   textInfoIndicesBegin,
332                                   textInfoIndicesEnd,
333                                   textInfoMergeIndicesBegin,
334                                   textInfoMergeIndicesEnd,
335                                   groupLayout,
336                                   removedTextActorsFromFirstWord );
337   } // word indices
338 }
339
340 void RemoveWordsFromWordGroup( const std::size_t wordIndex,
341                                const std::size_t numberOfWords,
342                                WordGroupLayoutInfo& wordGroupLayoutInfo )
343 {
344   // Removes words from a group of words.
345
346   // * Check if words or lines can be merged after removing a word or line separator have to be done outside this method.
347
348   // * Note: Currently it's only used to remove a number of words from the beginning, or
349   //         from wordIndex index to the end. This function doesn't merge words (if a white space is removed)
350   //         TODO: merge words if required.
351
352   const std::size_t wordEndIndex = wordIndex + numberOfWords;
353
354   // Remove words from layout info.
355   wordGroupLayoutInfo.mWordsLayoutInfo.erase( wordGroupLayoutInfo.mWordsLayoutInfo.begin() + wordIndex,
356                                               wordGroupLayoutInfo.mWordsLayoutInfo.begin() + wordEndIndex );
357
358   // update layout info
359   wordGroupLayoutInfo.mSize = Size();
360   wordGroupLayoutInfo.mAscender = 0.f;
361   wordGroupLayoutInfo.mNumberOfCharacters = 0;
362   for( WordLayoutInfoContainer::const_iterator it = wordGroupLayoutInfo.mWordsLayoutInfo.begin(), endIt = wordGroupLayoutInfo.mWordsLayoutInfo.end();
363        it != endIt;
364        ++it )
365   {
366     const WordLayoutInfo& info( *it );
367
368     UpdateSize( wordGroupLayoutInfo.mSize, info.mSize );
369     wordGroupLayoutInfo.mAscender = std::max( wordGroupLayoutInfo.mAscender, info.mAscender );
370     wordGroupLayoutInfo.mNumberOfCharacters += info.mCharactersLayoutInfo.size();
371   }
372 }
373
374 void SplitWordGroup( const TextInfoIndices& indices,
375                      WordGroupLayoutInfo& firstWordGroupLayoutInfo,
376                      WordGroupLayoutInfo& lastWordGroupLayoutInfo )
377 {
378   // Splits a group of words in two.
379   // A word may be split in two as well.
380
381   // * Split the word pointed by indices.mWordIndex using the indices.mCharacterIndex index.
382   // * Add the last part of the word as first word of the last part of the group of words.
383   // * Add folliwing words to the last part of the new group of words.
384   // * Remove from the first part of the group of words all words added to the last part of the group of words.
385   // * Update layout info.
386
387   //early returns
388   if( ( 0 == indices.mWordIndex ) && ( 0 == indices.mCharacterIndex ) )
389   {
390     // the whole group of words goes to the last part of the group.
391     lastWordGroupLayoutInfo = firstWordGroupLayoutInfo;
392
393     firstWordGroupLayoutInfo = WordGroupLayoutInfo();
394
395     return;
396   }
397
398   if( !firstWordGroupLayoutInfo.mWordsLayoutInfo.empty() )
399   {
400     const std::size_t numberOfWords = firstWordGroupLayoutInfo.mWordsLayoutInfo.size();
401     if( indices.mWordIndex == numberOfWords - 1 )
402     {
403       const WordLayoutInfo& word( *( firstWordGroupLayoutInfo.mWordsLayoutInfo.end() - 1 ) );
404       if( indices.mCharacterIndex == word.mCharactersLayoutInfo.size() )
405       {
406         // the whole group of words goes to the first part.
407
408         // Just delete whatever there is in the last part of the group of words.
409         lastWordGroupLayoutInfo = WordGroupLayoutInfo();
410
411         return;
412       }
413     }
414   }
415
416   lastWordGroupLayoutInfo = WordGroupLayoutInfo();
417
418   // 1) Split the word within the group of words to be split.
419   WordLayoutInfo& firstWordLayoutInfo( *( firstWordGroupLayoutInfo.mWordsLayoutInfo.begin() + indices.mWordIndex ) );
420   WordLayoutInfo lastWordLayoutInfo;
421
422   SplitWord( indices.mCharacterIndex,
423              firstWordLayoutInfo,
424              lastWordLayoutInfo );
425
426   // 2) Add last part of the word to the new group of words.
427   if( !lastWordLayoutInfo.mCharactersLayoutInfo.empty() )
428   {
429     lastWordGroupLayoutInfo.mWordsLayoutInfo.push_back( lastWordLayoutInfo );
430   }
431
432   // 3) Add words from word-position + 1 to the end.
433   lastWordGroupLayoutInfo.mWordsLayoutInfo.insert( lastWordGroupLayoutInfo.mWordsLayoutInfo.end(),
434                                                    firstWordGroupLayoutInfo.mWordsLayoutInfo.begin() + indices.mWordIndex + 1, firstWordGroupLayoutInfo.mWordsLayoutInfo.end() );
435
436   // 4) update layout info of the last group of words.
437   lastWordGroupLayoutInfo.mDirection = firstWordGroupLayoutInfo.mDirection;
438
439   for( WordLayoutInfoContainer::iterator it = lastWordGroupLayoutInfo.mWordsLayoutInfo.begin(), endIt = lastWordGroupLayoutInfo.mWordsLayoutInfo.end();
440        it != endIt;
441        ++it )
442   {
443     WordLayoutInfo& layoutInfo( *it );
444
445     UpdateSize( lastWordGroupLayoutInfo.mSize, layoutInfo.mSize );
446     lastWordGroupLayoutInfo.mNumberOfCharacters += layoutInfo.mCharactersLayoutInfo.size();
447     lastWordGroupLayoutInfo.mAscender = std::max( lastWordGroupLayoutInfo.mAscender, layoutInfo.mAscender );
448   }
449
450   // 5) Remove words added to the last part of the group of words from the first group of words.
451
452   // if the number of characters of the last word of the first group is zero, it should be removed.
453   const std::size_t index = ( firstWordLayoutInfo.mCharactersLayoutInfo.empty() ? indices.mWordIndex : indices.mWordIndex + 1 );
454
455   firstWordGroupLayoutInfo.mWordsLayoutInfo.erase( firstWordGroupLayoutInfo.mWordsLayoutInfo.begin() + index, firstWordGroupLayoutInfo.mWordsLayoutInfo.end() );
456
457   // 6) update layout info of the first group of words.
458   firstWordGroupLayoutInfo.mSize = Size();
459   firstWordGroupLayoutInfo.mAscender = 0.f;
460   firstWordGroupLayoutInfo.mNumberOfCharacters = 0;
461   for( WordLayoutInfoContainer::iterator it = firstWordGroupLayoutInfo.mWordsLayoutInfo.begin(), endIt = firstWordGroupLayoutInfo.mWordsLayoutInfo.end();
462        it != endIt;
463        ++it )
464   {
465     WordLayoutInfo& layoutInfo( *it );
466
467     UpdateSize( firstWordGroupLayoutInfo.mSize, layoutInfo.mSize );
468     firstWordGroupLayoutInfo.mNumberOfCharacters += layoutInfo.mCharactersLayoutInfo.size();
469     firstWordGroupLayoutInfo.mAscender = std::max( firstWordGroupLayoutInfo.mAscender, layoutInfo.mAscender );
470   }
471 }
472
473 void MergeWordGroup( WordGroupLayoutInfo& firstWordGroupLayoutInfo,
474                      const WordGroupLayoutInfo& lastWordGroupLayoutInfo )
475 {
476   // Merges two given groups of words.
477   //
478   // Can't merge two groups if they have text with different directions (RTL , LTR )
479   // or if the last word of the first one is a line separator (new line character)
480
481   // Early returns
482
483   if( lastWordGroupLayoutInfo.mWordsLayoutInfo.empty() )
484   {
485     // Nothing to merge if last group is empty.
486     return;
487   }
488
489   if( firstWordGroupLayoutInfo.mWordsLayoutInfo.empty() )
490   {
491     // If first group is empty, just copy the last group to the first one.
492     firstWordGroupLayoutInfo = lastWordGroupLayoutInfo;
493
494     return;
495   }
496
497   // Check both groups have the same direction.
498   if( firstWordGroupLayoutInfo.mDirection != lastWordGroupLayoutInfo.mDirection )
499   {
500     DALI_ASSERT_ALWAYS( !"TextViewProcessor::MergeWordGroup(). ERROR: groups with different direction can't be merged." );
501   }
502
503   // Check first group doesn't finish with a new line character.
504   WordLayoutInfo& lastWordLayout( *( firstWordGroupLayoutInfo.mWordsLayoutInfo.end() - 1 ) );
505   if( LineSeparator == lastWordLayout.mType )
506   {
507     DALI_ASSERT_ALWAYS( !"TextViewProcessor::MergeWordGroup(). ERROR: A group of words can't be merged to another group which finishes with a new line character." );
508   }
509
510   // If the las word of the first group or the first word of the last group is a white space, both groups can be concatenated.
511   // Otherwise both words need to be merged first.
512   const WordLayoutInfo& firstWordLayout( *lastWordGroupLayoutInfo.mWordsLayoutInfo.begin() );
513
514   std::size_t index = 0;
515   if( ( WordSeparator != lastWordLayout.mType ) && ( WordSeparator != firstWordLayout.mType ) && ( LineSeparator != firstWordLayout.mType ) )
516   {
517     // Last word of the first group is not a word separator and first word of the last group is not a word or line separator.
518     // Words need to be merged.
519
520     MergeWord( lastWordLayout,
521                firstWordLayout );
522
523     // After merging two words, the rest of the words need to be added.
524     ++index; // By increasing this index the word already merged won't be added again.
525   }
526
527   // Merge layout info
528   firstWordGroupLayoutInfo.mWordsLayoutInfo.insert( firstWordGroupLayoutInfo.mWordsLayoutInfo.end(),
529                                                     lastWordGroupLayoutInfo.mWordsLayoutInfo.begin() + index, lastWordGroupLayoutInfo.mWordsLayoutInfo.end() );
530   UpdateSize( firstWordGroupLayoutInfo.mSize, lastWordGroupLayoutInfo.mSize );
531   firstWordGroupLayoutInfo.mAscender = std::max( firstWordGroupLayoutInfo.mAscender, lastWordGroupLayoutInfo.mAscender );
532   firstWordGroupLayoutInfo.mNumberOfCharacters += lastWordGroupLayoutInfo.mNumberOfCharacters;
533 }
534
535 void CollectTextActorsFromGroups( std::vector<TextActor>& textActors, const LineLayoutInfo& line, const std::size_t groupIndexBegin, const std::size_t groupIndexEnd )
536 {
537   for( WordGroupLayoutInfoContainer::const_iterator groupIt = line.mWordGroupsLayoutInfo.begin() + groupIndexBegin, groupEndIt = line.mWordGroupsLayoutInfo.begin() + groupIndexEnd;
538        groupIt != groupEndIt;
539        ++groupIt )
540   {
541     const WordGroupLayoutInfo& group( *groupIt );
542
543     CollectTextActorsFromWords( textActors, group, 0, group.mWordsLayoutInfo.size() );
544   }
545 }
546
547 } //namespace TextViewProcessor
548
549 } //namespace Internal
550
551 } //namespace Toolkit
552
553 } //namespace Dali