94a2998fda894adf662addd6edb5e10d961878a7
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / text-view / text-view-word-processor.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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 // FILE HEADER
18 #include "text-view-word-processor.h"
19
20 // INTERNAL INCLUDES
21 #include <dali/dali.h>
22 #include "text-view-processor-helper-functions.h"
23 #include "text-view-processor-dbg.h"
24
25 namespace Dali
26 {
27
28 namespace Toolkit
29 {
30
31 namespace Internal
32 {
33
34 namespace TextViewProcessor
35 {
36
37 namespace
38 {
39
40 /**
41  * Updates the word size and ascender.
42  *
43  * It's called after deleting some characters.
44  *
45  * @param[in] wordLayout The word layout info.
46  */
47 void UpdateLayoutInfo( WordLayoutInfo& wordLayout )
48 {
49   // Initialize layout info for the whole word.
50   wordLayout.mSize = Size();
51   wordLayout.mAscender = 0.f;
52
53   // Traverse the character layout info to update the word layout.
54   for( CharacterLayoutInfoContainer::iterator layoutIt = wordLayout.mCharactersLayoutInfo.begin(), layoutEndIt = wordLayout.mCharactersLayoutInfo.end();
55        layoutIt != layoutEndIt;
56        ++layoutIt )
57   {
58     // Layout info for the current character.
59     CharacterLayoutInfo& layoutInfo( *layoutIt );
60
61     // Update layout info for the current word.
62     UpdateSize( wordLayout.mSize, layoutInfo.mSize );
63     wordLayout.mAscender = std::max( wordLayout.mAscender, layoutInfo.mAscender );
64   }
65 }
66
67 } // namespace
68
69 /////////////////////
70 // Layout info.
71 /////////////////////
72
73 WordLayoutInfo::WordLayoutInfo()
74 : mSize(),
75   mAscender( 0.f ),
76   mType( NoSeparator ),
77   mCharactersLayoutInfo()
78 {
79 }
80
81 WordLayoutInfo::WordLayoutInfo( const WordLayoutInfo& word )
82 : mSize( word.mSize ),
83   mAscender( word.mAscender ),
84   mType( word.mType ),
85   mCharactersLayoutInfo( word.mCharactersLayoutInfo )
86 {
87 }
88
89 WordLayoutInfo& WordLayoutInfo::operator=( const WordLayoutInfo& word )
90 {
91   mSize = word.mSize;
92   mAscender = word.mAscender;
93   mType = word.mType;
94   mCharactersLayoutInfo = word.mCharactersLayoutInfo;
95
96   return *this;
97 }
98
99 void CreateWordTextInfo( const MarkupProcessor::StyledTextArray& word,
100                          TextViewProcessor::WordLayoutInfo& wordLayoutInfo )
101 {
102   DALI_LOG_INFO( gTextViewProcessorLogFilter, Debug::General, "-->TextViewProcessor::CreateWordTextInfo\n" );
103   // Split in characters.
104   for( MarkupProcessor::StyledTextArray::const_iterator charIt = word.begin(), charEndIt = word.end(); charIt != charEndIt; ++charIt )
105   {
106     const MarkupProcessor::StyledText& styledText( *charIt );
107
108     const std::size_t length = styledText.mText.GetLength();
109
110     // It could be a group of characters.
111     for( std::size_t index = 0; index < length; ++index )
112     {
113       MarkupProcessor::StyledText styledCharacter;
114       styledCharacter.mStyle = styledText.mStyle;
115       Character character = styledText.mText[index];
116       styledCharacter.mText.Append( character );
117
118       //Choose the right font for the given character and style.
119       ChooseFontFamilyName( styledCharacter );
120
121       const Font font = Font::New( FontParameters( styledCharacter.mStyle.GetFontName(), styledCharacter.mStyle.GetFontStyle(), styledCharacter.mStyle.GetFontPointSize() ) );
122       const Font::Metrics metrics = font.GetMetrics( character );
123       const float ascender = font.GetAscender();
124
125       // Create layout character info.
126       CharacterLayoutInfo characterLayoutInfo;
127
128       characterLayoutInfo.mIsColorGlyph = GlyphImage::IsColorGlyph( character );
129       DALI_LOG_INFO( gTextViewProcessorLogFilter, Debug::General, "  Is color glyph: %s\n", ( characterLayoutInfo.mIsColorGlyph ? "True" : "False" ) );
130
131       // Fill Natural size info for current character.
132       characterLayoutInfo.mHeight = font.GetLineHeight();
133       characterLayoutInfo.mAdvance = metrics.GetAdvance();
134       characterLayoutInfo.mBearing = metrics.GetBearing();
135
136       if( character.IsNewLine() && !characterLayoutInfo.mIsColorGlyph )
137       {
138         // A new line character doesn't have any width.
139         characterLayoutInfo.mSize.width = 0.f;
140       }
141       else
142       {
143         // Uses advance as width.
144         characterLayoutInfo.mSize.width = characterLayoutInfo.mAdvance;
145       }
146       characterLayoutInfo.mSize.height = characterLayoutInfo.mHeight;
147       characterLayoutInfo.mAscender = ascender;
148
149       if( styledCharacter.mStyle.GetUnderline() )
150       {
151         characterLayoutInfo.mUnderlineThickness = font.GetUnderlineThickness(); // Both thickness and position includes the
152         characterLayoutInfo.mUnderlinePosition = font.GetUnderlinePosition();   // vertical pad adjust used in effects like glow or shadow.
153       }
154
155       // stores the styled text.
156       characterLayoutInfo.mStyledText.mText = styledCharacter.mText;
157       characterLayoutInfo.mStyledText.mStyle = styledCharacter.mStyle;
158
159       // Add character layout info to the word layout info and update it.
160       wordLayoutInfo.mCharactersLayoutInfo.push_back( characterLayoutInfo );
161       UpdateSize( wordLayoutInfo.mSize, characterLayoutInfo.mSize );
162       wordLayoutInfo.mAscender = std::max( wordLayoutInfo.mAscender, characterLayoutInfo.mAscender );
163       wordLayoutInfo.mType = GetTextSeparatorType( character );
164     } // end of each character in the group of characters.
165   } // end of characters in the word.
166   DALI_LOG_INFO( gTextViewProcessorLogFilter, Debug::General, "<--TextViewProcessor::CreateWordTextInfo\n" );
167 }
168
169 void RemoveCharactersFromWordInfo( TextView::RelayoutData& relayoutData,
170                                    const std::size_t numberOfCharacters,
171                                    bool& mergeWords,
172                                    bool& mergeLines,
173                                    TextViewProcessor::TextInfoIndices& textInfoIndicesBegin,
174                                    TextViewProcessor::TextInfoIndices& textInfoIndicesEnd,
175                                    TextViewProcessor::TextInfoIndices& textInfoMergeIndicesBegin,
176                                    TextViewProcessor::TextInfoIndices& textInfoMergeIndicesEnd,
177                                    TextViewProcessor::WordGroupLayoutInfo& groupLayout,
178                                    std::vector<TextActor>& removedTextActors )
179 {
180   const TextViewProcessor::TextLayoutInfo& textLayoutInfo = relayoutData.mTextLayoutInfo;
181
182   // Get the word.
183   WordLayoutInfo& wordLayout( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex ) );
184
185   if( TextViewProcessor::LineSeparator == wordLayout.mType )
186   {
187     // If the word is a line separator and there is more lines, then current line and the line after need to be merged.
188     if( textInfoIndicesBegin.mLineIndex + 1 < textLayoutInfo.mLinesLayoutInfo.size() )
189     {
190       // current line is not the last one.
191
192       // Update indices to merge lines.
193       textInfoMergeIndicesBegin.mLineIndex = textInfoIndicesBegin.mLineIndex;
194       textInfoMergeIndicesEnd.mLineIndex = textInfoIndicesBegin.mLineIndex + 1;
195
196       mergeLines = true;
197
198       ++textInfoIndicesBegin.mLineIndex; // increase both indices,
199       textInfoIndicesEnd.mLineIndex +=2; // will delete last line.
200     }
201
202     ++textInfoIndicesEnd.mWordIndex; //will delete the line separator;
203   }
204   else if( WordSeparator == wordLayout.mType )
205   {
206     // If the word is a word separator. Check if the word before and the word after can be merged.
207
208     if( ( 0 < textInfoIndicesBegin.mWordIndex ) && ( groupLayout.mWordsLayoutInfo.size() > textInfoIndicesBegin.mWordIndex + 1 ) )
209     {
210       const WordLayoutInfo& wordLayoutBefore( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex - 1 ) );
211       const WordLayoutInfo& wordLayoutAfter( *( groupLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex + 1 ) );
212
213       if( ( NoSeparator == wordLayoutBefore.mType ) && ( NoSeparator == wordLayoutAfter.mType ) )
214       {
215         // This word is a word separator (white space) and is not the first word of the group nor the last one.
216         mergeWords = true;
217
218         // Set indices to merge the words.
219         textInfoMergeIndicesBegin.mWordIndex = textInfoIndicesBegin.mWordIndex - 1; // word before word separator.
220         textInfoMergeIndicesEnd.mWordIndex = textInfoIndicesBegin.mWordIndex + 1; // word after word separator.
221
222         textInfoIndicesEnd.mWordIndex += 2; // will delete the word separator and the merged word.
223       }
224       else
225       {
226         ++textInfoIndicesEnd.mWordIndex; // will delete the word separator;
227       }
228     }
229     else
230     {
231       ++textInfoIndicesEnd.mWordIndex; // will delete the word separator;
232     }
233   }
234   else if( numberOfCharacters == wordLayout.mCharactersLayoutInfo.size() )
235   {
236     // The whole word needs to be removed.
237     ++textInfoIndicesEnd.mWordIndex; // will delete the current word.
238   }
239   else
240   {
241     // Store text-actors before removing them.
242     CollectTextActors( removedTextActors, wordLayout, textInfoIndicesBegin.mCharacterIndex, textInfoIndicesBegin.mCharacterIndex + numberOfCharacters );
243
244     // just remove some characters from current word.
245     RemoveCharactersFromWord( textInfoIndicesBegin.mCharacterIndex,
246                               numberOfCharacters,
247                               wordLayout );
248   }
249 }
250
251 void RemoveCharactersFromWord( const std::size_t position,
252                                const std::size_t numberOfCharacters,
253                                WordLayoutInfo& wordLayout )
254 {
255   // Removes a given number of characters from the given word starting from the 'position' index.
256
257   // Early return.
258   if( 0 == numberOfCharacters )
259   {
260     // nothing to do if the number of characters is zero.
261
262     return;
263   }
264
265   // Remove characters from layout and text-actor info.
266   wordLayout.mCharactersLayoutInfo.erase( wordLayout.mCharactersLayoutInfo.begin() + position, wordLayout.mCharactersLayoutInfo.begin() + position + numberOfCharacters );
267
268   // Some characters have been removed from the word. Update the layout info is needed.
269   UpdateLayoutInfo( wordLayout );
270 }
271
272 void SplitWord( const std::size_t position,
273                 WordLayoutInfo& firstWordLayoutInfo,
274                 WordLayoutInfo& lastWordLayoutInfo )
275 {
276   // Splits a word in two.
277   // It moves characters from the first part of the word to the last one.
278
279   // early returns
280   if( 0 == position )
281   {
282     // the whole word goes to the last part of the word.
283     lastWordLayoutInfo = firstWordLayoutInfo;
284
285     firstWordLayoutInfo = WordLayoutInfo();
286
287     return;
288   }
289
290   if( position == firstWordLayoutInfo.mCharactersLayoutInfo.size() )
291   {
292     // the whole word goes to the first part of the word.
293
294     // Just delete whatever there is in the last part of the word.
295     lastWordLayoutInfo = WordLayoutInfo();
296
297     return;
298   }
299
300   // Initialize output data structures.
301
302   // Layout info
303   lastWordLayoutInfo = WordLayoutInfo();
304
305   // Split layout info.
306
307   // Insert characters from the given index 'position' to the end.
308   lastWordLayoutInfo.mCharactersLayoutInfo.insert( lastWordLayoutInfo.mCharactersLayoutInfo.end(),
309                                                    firstWordLayoutInfo.mCharactersLayoutInfo.begin() + position, firstWordLayoutInfo.mCharactersLayoutInfo.end() );
310
311   // Delete characters from the first part of the word.
312   firstWordLayoutInfo.mCharactersLayoutInfo.erase( firstWordLayoutInfo.mCharactersLayoutInfo.begin() + position, firstWordLayoutInfo.mCharactersLayoutInfo.end() );
313
314   // Update the layout info of both new words.
315   UpdateLayoutInfo( firstWordLayoutInfo );
316   UpdateLayoutInfo( lastWordLayoutInfo );
317 }
318
319 void MergeWord( WordLayoutInfo& firstWordLayoutInfo,
320                 const WordLayoutInfo& lastWordLayoutInfo )
321 {
322   // Merges two given words.
323
324   // Early returns.
325   if( lastWordLayoutInfo.mCharactersLayoutInfo.empty() )
326   {
327     // nothing to do
328     return;
329   }
330
331   if( firstWordLayoutInfo.mCharactersLayoutInfo.empty() )
332   {
333     // copy last to first
334
335     firstWordLayoutInfo = lastWordLayoutInfo;
336
337     return;
338   }
339
340   if( ( NoSeparator != firstWordLayoutInfo.mType ) || ( NoSeparator != lastWordLayoutInfo.mType ) )
341   {
342     // Do not merge white spaces or new line characters.
343     DALI_ASSERT_ALWAYS( !"TextViewProcessor::MergeWord(). ERROR: White spaces or new line characters can't be merged with other words." );
344   }
345
346   // Merge layout info
347   firstWordLayoutInfo.mCharactersLayoutInfo.insert( firstWordLayoutInfo.mCharactersLayoutInfo.end(),
348                                                     lastWordLayoutInfo.mCharactersLayoutInfo.begin(),
349                                                     lastWordLayoutInfo.mCharactersLayoutInfo.end() );
350
351   // Update the word layout info.
352   UpdateSize( firstWordLayoutInfo.mSize, lastWordLayoutInfo.mSize );
353   firstWordLayoutInfo.mAscender = std::max( firstWordLayoutInfo.mAscender, lastWordLayoutInfo.mAscender );
354 }
355
356 CharacterLayoutInfo GetFirstCharacterLayoutInfo( const WordLayoutInfo& wordLayoutInfo )
357 {
358   CharacterLayoutInfo layoutInfo;
359
360   if( !wordLayoutInfo.mCharactersLayoutInfo.empty() )
361   {
362     layoutInfo = *wordLayoutInfo.mCharactersLayoutInfo.begin();
363   }
364
365   return layoutInfo;
366 }
367
368 CharacterLayoutInfo GetLastCharacterLayoutInfo( const WordLayoutInfo& wordLayoutInfo )
369 {
370   CharacterLayoutInfo layoutInfo;
371
372   if( !wordLayoutInfo.mCharactersLayoutInfo.empty() )
373   {
374     layoutInfo = *( wordLayoutInfo.mCharactersLayoutInfo.end() - 1 );
375   }
376
377   return layoutInfo;
378 }
379
380 void CollectTextActors( std::vector<TextActor>& textActors, const WordLayoutInfo& word, const std::size_t characterIndexBegin, const std::size_t characterIndexEnd )
381 {
382   for( CharacterLayoutInfoContainer::const_iterator characterIt = word.mCharactersLayoutInfo.begin() + characterIndexBegin,
383          characterEndIt = word.mCharactersLayoutInfo.begin() + characterIndexEnd;
384        characterIt != characterEndIt;
385        ++characterIt )
386   {
387     const CharacterLayoutInfo& characterLayout( *characterIt );
388
389     if( !characterLayout.mIsColorGlyph )
390     {
391       TextActor textActor = TextActor::DownCast( characterLayout.mGlyphActor );
392       if( textActor )
393       {
394         textActors.push_back( textActor );
395       }
396     }
397   }
398 }
399
400 void CollectTextActorsFromWords( std::vector<TextActor>& textActors, const WordGroupLayoutInfo& group, const std::size_t wordIndexBegin, const std::size_t wordIndexEnd )
401 {
402   for( WordLayoutInfoContainer::const_iterator wordIt = group.mWordsLayoutInfo.begin() + wordIndexBegin, wordEndIt = group.mWordsLayoutInfo.begin() + wordIndexEnd;
403        wordIt != wordEndIt;
404        ++wordIt )
405   {
406     const WordLayoutInfo& word( *wordIt );
407
408     for( CharacterLayoutInfoContainer::const_iterator characterIt = word.mCharactersLayoutInfo.begin(), characterEndIt = word.mCharactersLayoutInfo.end();
409          characterIt != characterEndIt;
410          ++characterIt )
411     {
412       const CharacterLayoutInfo& characterLayout( *characterIt );
413
414       if( !characterLayout.mIsColorGlyph )
415       {
416         TextActor textActor = TextActor::DownCast( characterLayout.mGlyphActor );
417         if( textActor )
418         {
419           textActors.push_back( textActor );
420         }
421       }
422     }
423   }
424 }
425
426 } //namespace TextViewProcessor
427
428 } //namespace Internal
429
430 } //namespace Toolkit
431
432 } //namespace Dali