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