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