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