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