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