GetNaturalSize() to restore ControlSize after relayouting, Scrolling Gap from float...
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / logical-model-impl.cpp
1 /*
2  * Copyright (c) 2015 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 // CLASS HEADER
19 #include <dali-toolkit/internal/text/logical-model-impl.h>
20
21 // INTERNAL INCLUDES
22 #include <dali-toolkit/internal/text/input-style.h>
23 #include <dali-toolkit/internal/text/text-run-container.h>
24
25 namespace Dali
26 {
27
28 namespace Toolkit
29 {
30
31 namespace Text
32 {
33
34 void FreeFontFamilyNames( Vector<FontDescriptionRun>& fontDescriptionRuns )
35 {
36   for( Vector<FontDescriptionRun>::Iterator it = fontDescriptionRuns.Begin(),
37          endIt = fontDescriptionRuns.End();
38        it != endIt;
39        ++it )
40   {
41     delete (*it).familyName;
42   }
43
44   fontDescriptionRuns.Clear();
45 }
46
47 LogicalModelPtr LogicalModel::New()
48 {
49   return LogicalModelPtr( new LogicalModel() );
50 }
51
52 Script LogicalModel::GetScript( CharacterIndex characterIndex ) const
53 {
54   // If this operation is too slow, consider a binary search.
55
56   for( Length index = 0u, length = mScriptRuns.Count(); index < length; ++index )
57   {
58     const ScriptRun* const scriptRun = mScriptRuns.Begin() + index;
59
60     if( ( scriptRun->characterRun.characterIndex <= characterIndex ) &&
61         ( characterIndex < scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters ) )
62     {
63       return scriptRun->script;
64     }
65   }
66
67   return TextAbstraction::UNKNOWN;
68 }
69
70 CharacterDirection LogicalModel::GetCharacterDirection( CharacterIndex characterIndex ) const
71 {
72   if( characterIndex >= mCharacterDirections.Count() )
73   {
74     // The model has no right to left characters, so the vector of directions is void.
75     return false;
76   }
77
78   return *( mCharacterDirections.Begin() + characterIndex );
79 }
80
81 CharacterIndex LogicalModel::GetLogicalCursorIndex( CharacterIndex visualCursorIndex )
82 {
83   // The total number of characters.
84   const Length numberOfCharacters = mText.Count();
85
86   const bool fetch = FetchBidirectionalLineInfo( visualCursorIndex );
87   if( !fetch )
88   {
89     // The character is not inside a bidi line.
90     return visualCursorIndex;
91   }
92
93   // The character's directions buffer.
94   const CharacterDirection* const modelCharacterDirections = mCharacterDirections.Begin();
95
96   // The bidirectional line info.
97   const BidirectionalLineInfoRun* const bidirectionalLineInfo = mBidirectionalLineInfo.Begin() + mBidirectionalLineIndex;
98
99   // Whether the paragraph starts with a right to left character.
100   const bool isRightToLeftParagraph = bidirectionalLineInfo->direction;
101
102   CharacterIndex logicalCursorIndex = 0u;
103
104   if( 0u == visualCursorIndex )
105   {
106     if( isRightToLeftParagraph )
107     {
108       logicalCursorIndex = numberOfCharacters;
109     }
110     else // else logical position is zero.
111     {
112       logicalCursorIndex = 0u;
113     }
114   }
115   else if( numberOfCharacters == visualCursorIndex )
116   {
117     if( isRightToLeftParagraph )
118     {
119       logicalCursorIndex = 0u;
120     }
121     else // else logical position is the number of characters.
122     {
123       logicalCursorIndex = numberOfCharacters;
124     }
125   }
126   else
127   {
128     // Get the character indexed by  index - 1 and index
129     // and calculate the logical position according the directions of
130     // both characters and the direction of the paragraph.
131
132     const CharacterIndex previousVisualCursorIndex = visualCursorIndex - 1u;
133     const CharacterIndex previousLogicalCursorIndex = *( bidirectionalLineInfo->visualToLogicalMap + previousVisualCursorIndex - bidirectionalLineInfo->characterRun.characterIndex ) + bidirectionalLineInfo->characterRun.characterIndex;
134     const CharacterIndex currentLogicalCursorIndex = *( bidirectionalLineInfo->visualToLogicalMap + visualCursorIndex - bidirectionalLineInfo->characterRun.characterIndex ) + bidirectionalLineInfo->characterRun.characterIndex;
135
136     const CharacterDirection previousCharacterDirection = *( modelCharacterDirections + previousLogicalCursorIndex );
137     const CharacterDirection currentCharacterDirection = *( modelCharacterDirections + currentLogicalCursorIndex );
138
139     if( previousCharacterDirection == currentCharacterDirection )
140     {
141       // Both glyphs have the same direction.
142       if( previousCharacterDirection )
143       {
144         logicalCursorIndex = previousLogicalCursorIndex;
145       }
146       else
147       {
148         logicalCursorIndex = currentLogicalCursorIndex;
149       }
150     }
151     else
152     {
153       if( isRightToLeftParagraph )
154       {
155         if( currentCharacterDirection )
156         {
157           logicalCursorIndex = currentLogicalCursorIndex + 1u;
158         }
159         else
160         {
161           logicalCursorIndex = previousLogicalCursorIndex;
162         }
163       }
164       else
165       {
166         if( previousCharacterDirection )
167         {
168           logicalCursorIndex = currentLogicalCursorIndex;
169         }
170         else
171         {
172           logicalCursorIndex = previousLogicalCursorIndex + 1u;
173         }
174       }
175     }
176   }
177
178   return logicalCursorIndex;
179 }
180
181 CharacterIndex LogicalModel::GetLogicalCharacterIndex( CharacterIndex visualCharacterIndex )
182 {
183   const bool fetch = FetchBidirectionalLineInfo( visualCharacterIndex );
184   if( !fetch )
185   {
186     // The character is not inside a bidi line.
187     return visualCharacterIndex;
188   }
189
190   // The bidirectional line info.
191   const BidirectionalLineInfoRun* const bidirectionalLineInfo = mBidirectionalLineInfo.Begin() + mBidirectionalLineIndex;
192
193   return *( bidirectionalLineInfo->visualToLogicalMap + visualCharacterIndex - bidirectionalLineInfo->characterRun.characterIndex ) + bidirectionalLineInfo->characterRun.characterIndex;
194 }
195
196 bool LogicalModel::FetchBidirectionalLineInfo( CharacterIndex characterIndex )
197 {
198   // The number of bidirectional lines.
199   const Length numberOfBidirectionalLines = mBidirectionalLineInfo.Count();
200
201   if( 0u == numberOfBidirectionalLines )
202   {
203     // If there is no bidirectional info.
204     return false;
205   }
206
207   // Find the bidi line where the character is laid-out.
208
209   const BidirectionalLineInfoRun* const bidirectionalLineInfoBuffer = mBidirectionalLineInfo.Begin();
210
211   // Check first if the character is in the previously fetched line.
212
213   BidirectionalLineRunIndex bidiLineIndex = 0u;
214   CharacterIndex lastCharacterOfRightToLeftRun = 0u;
215   if( mBidirectionalLineIndex < numberOfBidirectionalLines )
216   {
217     const BidirectionalLineInfoRun& bidiLineRun = *( bidirectionalLineInfoBuffer + mBidirectionalLineIndex );
218
219     // Whether the character index is just after the last one. i.e The cursor position after the last character.
220     const bool isLastIndex = characterIndex == mText.Count();
221
222     const CharacterIndex lastCharacterOfRunPlusOne = bidiLineRun.characterRun.characterIndex + bidiLineRun.characterRun.numberOfCharacters;
223     if( ( bidiLineRun.characterRun.characterIndex <= characterIndex ) &&
224         ( ( characterIndex < lastCharacterOfRunPlusOne ) || ( isLastIndex && ( characterIndex == lastCharacterOfRunPlusOne ) ) ) )
225     {
226       // The character is in the previously fetched bidi line.
227       return true;
228     }
229     else
230     {
231       // The character is not in the previously fetched line.
232
233       if( isLastIndex )
234       {
235         // The given index is one after the last character, so it's not in a bidi line.
236         // Check if it's just after the last bidi line.
237         const BidirectionalLineRunIndex lastBidiLineIndex = numberOfBidirectionalLines - 1u;
238         const BidirectionalLineInfoRun& bidiLineRun = *( bidirectionalLineInfoBuffer + lastBidiLineIndex );
239
240         if( characterIndex == bidiLineRun.characterRun.characterIndex + bidiLineRun.characterRun.numberOfCharacters )
241         {
242           // The character is in the last bidi line.
243           mBidirectionalLineIndex = lastBidiLineIndex;
244           return true;
245         }
246       }
247
248       // Set the bidi line index from where to start the fetch.
249
250       if( characterIndex < bidiLineRun.characterRun.characterIndex )
251       {
252         // Start the fetch from the beginning.
253         bidiLineIndex = 0u;
254       }
255       else
256       {
257         // Start the fetch from the next line.
258         bidiLineIndex = mBidirectionalLineIndex + 1u;
259         lastCharacterOfRightToLeftRun = lastCharacterOfRunPlusOne - 1u;
260       }
261     }
262   }
263
264   // The character has not been found in the previously fetched bidi line.
265   for( Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLineInfoBuffer + bidiLineIndex,
266          endIt = mBidirectionalLineInfo.End();
267        it != endIt;
268        ++it, ++bidiLineIndex )
269   {
270     const BidirectionalLineInfoRun& bidiLineRun = *it;
271
272     if( ( lastCharacterOfRightToLeftRun < characterIndex ) && ( characterIndex < bidiLineRun.characterRun.characterIndex ) )
273     {
274       // The character is not inside a bidi line.
275       return false;
276     }
277
278     const CharacterIndex lastCharacterOfRunPlusOne = bidiLineRun.characterRun.characterIndex + bidiLineRun.characterRun.numberOfCharacters;
279     lastCharacterOfRightToLeftRun = lastCharacterOfRunPlusOne - 1u;
280     if( ( bidiLineRun.characterRun.characterIndex <= characterIndex ) &&
281         ( characterIndex < lastCharacterOfRunPlusOne ) )
282     {
283       // Bidi line found. Fetch the line.
284       mBidirectionalLineIndex = bidiLineIndex;
285       return true;
286     }
287   }
288
289   return false;
290 }
291
292 BidirectionalLineRunIndex LogicalModel::GetBidirectionalLineInfo() const
293 {
294   return mBidirectionalLineIndex;
295 }
296
297 void LogicalModel::UpdateTextStyleRuns( CharacterIndex index, int numberOfCharacters )
298 {
299   const Length totalNumberOfCharacters = mText.Count();
300
301   // Process the color runs.
302   Vector<ColorRun> removedColorRuns;
303   UpdateCharacterRuns<ColorRun>( index,
304                                  numberOfCharacters,
305                                  totalNumberOfCharacters,
306                                  mColorRuns,
307                                  removedColorRuns );
308
309   // Process the font description runs.
310   Vector<FontDescriptionRun> removedFontDescriptionRuns;
311   UpdateCharacterRuns<FontDescriptionRun>( index,
312                                            numberOfCharacters,
313                                            totalNumberOfCharacters,
314                                            mFontDescriptionRuns,
315                                            removedFontDescriptionRuns );
316
317   // Free memory allocated for the font family name.
318   FreeFontFamilyNames( removedFontDescriptionRuns );
319 }
320
321 void LogicalModel::RetrieveStyle( CharacterIndex index, InputStyle& style )
322 {
323   unsigned int runIndex = 0u;
324
325   // Set the text color.
326   bool colorOverriden = false;
327   unsigned int colorIndex = 0u;
328   const ColorRun* const colorRunsBuffer = mColorRuns.Begin();
329   for( Vector<ColorRun>::ConstIterator it = colorRunsBuffer,
330          endIt = mColorRuns.End();
331        it != endIt;
332        ++it, ++runIndex )
333   {
334     const ColorRun& colorRun = *it;
335
336     if( ( colorRun.characterRun.characterIndex <= index ) &&
337         ( index < colorRun.characterRun.characterIndex + colorRun.characterRun.numberOfCharacters ) )
338     {
339       colorIndex = runIndex;
340       colorOverriden = true;
341     }
342   }
343
344   // Set the text's color if it's overriden.
345   if( colorOverriden )
346   {
347     style.textColor = ( *( colorRunsBuffer + colorIndex ) ).color;
348     style.isDefaultColor = false;
349   }
350
351   // Reset the run index.
352   runIndex = 0u;
353
354   // Set the font's parameters.
355   bool nameOverriden = false;
356   bool weightOverriden = false;
357   bool widthOverriden = false;
358   bool slantOverriden = false;
359   bool sizeOverriden = false;
360   unsigned int nameIndex = 0u;
361   unsigned int weightIndex = 0u;
362   unsigned int widthIndex = 0u;
363   unsigned int slantIndex = 0u;
364   unsigned int sizeIndex = 0u;
365   const FontDescriptionRun* const fontDescriptionRunsBuffer = mFontDescriptionRuns.Begin();
366   for( Vector<FontDescriptionRun>::ConstIterator it = fontDescriptionRunsBuffer,
367          endIt = mFontDescriptionRuns.End();
368        it != endIt;
369        ++it, ++runIndex )
370   {
371     const FontDescriptionRun& fontDescriptionRun = *it;
372
373     if( ( fontDescriptionRun.characterRun.characterIndex <= index ) &&
374         ( index < fontDescriptionRun.characterRun.characterIndex + fontDescriptionRun.characterRun.numberOfCharacters ) )
375     {
376       if( fontDescriptionRun.familyDefined )
377       {
378         nameIndex = runIndex;
379         nameOverriden = true;
380       }
381
382       if( fontDescriptionRun.weightDefined )
383       {
384         weightIndex = runIndex;
385         weightOverriden = true;
386       }
387
388       if( fontDescriptionRun.widthDefined )
389       {
390         widthIndex = runIndex;
391         widthOverriden = true;
392       }
393
394       if( fontDescriptionRun.slantDefined )
395       {
396         slantIndex = runIndex;
397         slantOverriden = true;
398       }
399
400       if( fontDescriptionRun.sizeDefined )
401       {
402         sizeIndex = runIndex;
403         sizeOverriden = true;
404       }
405     }
406   }
407
408   // Set the font's family name if it's overriden.
409   if( nameOverriden )
410   {
411     const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + nameIndex );
412
413     style.familyName = std::string( fontDescriptionRun.familyName, fontDescriptionRun.familyLength );
414     style.familyDefined = true;
415   }
416
417   // Set the font's weight if it's overriden.
418   if( weightOverriden )
419   {
420     const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + weightIndex );
421
422     style.weight = fontDescriptionRun.weight;
423     style.weightDefined = true;
424   }
425
426   // Set the font's width if it's overriden.
427   if( widthOverriden )
428   {
429     const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + widthIndex );
430
431     style.width = fontDescriptionRun.width;
432     style.widthDefined = true;
433   }
434
435   // Set the font's slant if it's overriden.
436   if( slantOverriden )
437   {
438     const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + slantIndex );
439
440     style.slant = fontDescriptionRun.slant;
441     style.slantDefined = true;
442   }
443
444   // Set the font's size if it's overriden.
445   if( sizeOverriden )
446   {
447     const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + sizeIndex );
448
449     style.size = static_cast<float>( fontDescriptionRun.size ) / 64.f;
450     style.sizeDefined = true;
451   }
452 }
453
454 void LogicalModel::ClearFontDescriptionRuns()
455 {
456   FreeFontFamilyNames( mFontDescriptionRuns );
457 }
458
459 void LogicalModel::CreateParagraphInfo( CharacterIndex startIndex,
460                                         Length numberOfCharacters )
461 {
462   const Length totalNumberOfCharacters = mLineBreakInfo.Count();
463
464   // Count the number of LINE_MUST_BREAK to reserve some space for the vector of paragraph's info.
465   Vector<CharacterIndex> paragraphs;
466   paragraphs.Reserve( numberOfCharacters );
467   const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = mLineBreakInfo.Begin();
468   const CharacterIndex lastCharacterIndexPlusOne = startIndex + numberOfCharacters;
469   for( Length index = startIndex; index < lastCharacterIndexPlusOne; ++index )
470   {
471     if( TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + index ) )
472     {
473       paragraphs.PushBack( index );
474     }
475   }
476
477   // Whether the current paragraphs are updated or set from scratch.
478   const bool updateCurrentParagraphs = numberOfCharacters < totalNumberOfCharacters;
479
480   // Reserve space for current paragraphs plus new ones.
481   const Length numberOfNewParagraphs = paragraphs.Count();
482   const Length totalNumberOfParagraphs = mParagraphInfo.Count() + numberOfNewParagraphs;
483   mParagraphInfo.Resize( totalNumberOfParagraphs );
484
485   ParagraphRun* paragraphInfoBuffer = NULL;
486   Vector<ParagraphRun> newParagraphs;
487
488   if( updateCurrentParagraphs )
489   {
490     newParagraphs.Resize( numberOfNewParagraphs );
491     paragraphInfoBuffer = newParagraphs.Begin();
492   }
493   else
494   {
495     paragraphInfoBuffer = mParagraphInfo.Begin();
496   }
497
498   // Find where to insert the new paragraphs.
499   ParagraphRunIndex paragraphIndex = 0u;
500   CharacterIndex firstIndex = startIndex;
501
502   if( updateCurrentParagraphs )
503   {
504     for( Vector<ParagraphRun>::ConstIterator it = mParagraphInfo.Begin(),
505            endIt = mParagraphInfo.Begin() + totalNumberOfParagraphs - numberOfNewParagraphs;
506          it != endIt;
507          ++it )
508     {
509       const ParagraphRun& paragraph( *it );
510
511       if( startIndex < paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
512       {
513         firstIndex = paragraph.characterRun.characterIndex;
514         break;
515       }
516
517       ++paragraphIndex;
518     }
519   }
520
521   // Create the paragraph info.
522   ParagraphRunIndex newParagraphIndex = 0u;
523   for( Vector<CharacterIndex>::ConstIterator it = paragraphs.Begin(),
524          endIt = paragraphs.End();
525        it != endIt;
526        ++it, ++newParagraphIndex )
527   {
528     const CharacterIndex index = *it;
529
530     ParagraphRun& paragraph = *( paragraphInfoBuffer + newParagraphIndex );
531     paragraph.characterRun.characterIndex = firstIndex;
532     paragraph.characterRun.numberOfCharacters = 1u + index - firstIndex;
533
534     firstIndex += paragraph.characterRun.numberOfCharacters;
535   }
536
537
538   // Insert the new paragraphs.
539   if( updateCurrentParagraphs )
540   {
541     mParagraphInfo.Insert( mParagraphInfo.Begin() + paragraphIndex,
542                            newParagraphs.Begin(),
543                            newParagraphs.End() );
544
545     mParagraphInfo.Resize( totalNumberOfParagraphs );
546
547     // Update the next paragraph indices.
548     for( Vector<ParagraphRun>::Iterator it = mParagraphInfo.Begin() + paragraphIndex + newParagraphs.Count(),
549            endIt = mParagraphInfo.End();
550          it != endIt;
551          ++it )
552     {
553       ParagraphRun& paragraph( *it );
554
555       paragraph.characterRun.characterIndex += numberOfCharacters;
556     }
557   }
558 }
559
560 void LogicalModel::FindParagraphs( CharacterIndex index,
561                                    Length numberOfCharacters,
562                                    Vector<ParagraphRunIndex>& paragraphs )
563 {
564   // Reserve som space for the paragraph indices.
565   paragraphs.Reserve( mParagraphInfo.Count() );
566
567   // Traverse the paragraphs to find which ones contain the given characters.
568   ParagraphRunIndex paragraphIndex = 0u;
569   for( Vector<ParagraphRun>::ConstIterator it = mParagraphInfo.Begin(),
570          endIt = mParagraphInfo.End();
571        it != endIt;
572        ++it, ++paragraphIndex )
573   {
574     const ParagraphRun& paragraph( *it );
575
576     if( ( paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters > index ) &&
577         ( paragraph.characterRun.characterIndex < index + numberOfCharacters ) )
578     {
579       paragraphs.PushBack( paragraphIndex );
580     }
581   }
582 }
583
584 LogicalModel::~LogicalModel()
585 {
586   ClearFontDescriptionRuns();
587 }
588
589 LogicalModel::LogicalModel()
590 : mBidirectionalLineIndex( 0u )
591 {
592 }
593
594 } // namespace Text
595
596 } // namespace Toolkit
597
598 } // namespace Dali