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