c61e0040f17aba8c5ba8036555e5cb49e1c91ee3
[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 void LogicalModel::SetVisualToLogicalMap( const BidirectionalLineInfoRun* const bidirectionalInfo,
82                                           Length numberOfRuns,
83                                           CharacterIndex startIndex,
84                                           Length numberOfCharacters )
85 {
86   mVisualToLogicalMap.Resize( numberOfCharacters );
87   mLogicalToVisualMap.Resize( numberOfCharacters );
88
89   const Length numberOfCharactersPlus = numberOfCharacters + 1u;
90   mVisualToLogicalCursorMap.Resize( numberOfCharactersPlus );
91
92   CharacterIndex* modelVisualToLogicalMapBuffer = mVisualToLogicalMap.Begin();
93   CharacterIndex* modelLogicalToVisualMapBuffer = mLogicalToVisualMap.Begin();
94
95   CharacterIndex* modelVisualToLogicalCursorMap = mVisualToLogicalCursorMap.Begin();
96
97   CharacterIndex lastIndex = startIndex;
98   for( unsigned int bidiIndex = 0u; bidiIndex < numberOfRuns; ++bidiIndex )
99   {
100     const BidirectionalLineInfoRun& bidiLineInfo = *( bidirectionalInfo + bidiIndex );
101
102     if( bidiLineInfo.characterRun.characterIndex + bidiLineInfo.characterRun.numberOfCharacters <= startIndex )
103     {
104       // Skip this paragraph. It has been already processed.
105       continue;
106     }
107
108     if( lastIndex < bidiLineInfo.characterRun.characterIndex )
109     {
110       // Fill with the identity.
111       for( ; lastIndex < bidiLineInfo.characterRun.characterIndex; ++lastIndex )
112       {
113         *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex;
114       }
115     }
116
117     // Fill the conversion table of the run.
118     for( CharacterIndex index = 0u;
119          index < bidiLineInfo.characterRun.numberOfCharacters;
120          ++index, ++lastIndex )
121     {
122       *( modelVisualToLogicalMapBuffer + lastIndex ) = bidiLineInfo.characterRun.characterIndex + *( bidiLineInfo.visualToLogicalMap + index );
123     }
124   }
125
126   // Complete with the identity if there are some left to right characters after the last right to left.
127   for( ; lastIndex < numberOfCharacters; ++lastIndex )
128   {
129     *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex;
130   }
131
132   // Sets the logical to visual conversion map.
133   for( CharacterIndex index = startIndex; index < numberOfCharacters; ++index )
134   {
135     *( modelLogicalToVisualMapBuffer + *( modelVisualToLogicalMapBuffer + index ) ) = index;
136   }
137
138   // Sets the visual to logical conversion map for cursor positions.
139
140   const Length numberOfBidirectionalParagraphs = mBidirectionalParagraphInfo.Count();
141   BidirectionalParagraphInfoRun* bidirectionalParagraphInfoBuffer = mBidirectionalParagraphInfo.Begin();
142   BidirectionalParagraphInfoRun* bidirectionalParagraph = bidirectionalParagraphInfoBuffer;
143
144   const CharacterDirection* const modelCharacterDirections = mCharacterDirections.Begin();
145
146   Length bidirectionalParagraphIndex = 0u;
147   bool isRightToLeftParagraph = false;
148   for( CharacterIndex index = startIndex; index < numberOfCharactersPlus; ++index )
149   {
150     if( bidirectionalParagraph &&
151         ( bidirectionalParagraph->characterRun.characterIndex == index ) )
152     {
153       isRightToLeftParagraph = *( modelCharacterDirections + index );
154     }
155
156     if( 0u == index )
157     {
158       if( isRightToLeftParagraph )
159       {
160         *( modelVisualToLogicalCursorMap + index ) = numberOfCharacters;
161       }
162       else // else logical position is zero.
163       {
164         *( modelVisualToLogicalCursorMap + index ) = 0u;
165       }
166     }
167     else if( numberOfCharacters == index )
168     {
169       if( isRightToLeftParagraph )
170       {
171         *( modelVisualToLogicalCursorMap + index ) = 0u;
172       }
173       else // else logical position is the number of characters.
174       {
175         *( modelVisualToLogicalCursorMap + index ) = numberOfCharacters;
176       }
177     }
178     else
179     {
180       // Get the character indexed by  index - 1 and index
181       // and calculate the logical position according the directions of
182       // both characters and the direction of the paragraph.
183
184       const CharacterIndex previousIndex = index - 1u;
185       const CharacterIndex logicalPosition0 = *( modelVisualToLogicalMapBuffer + previousIndex );
186       const CharacterIndex logicalPosition1 = *( modelVisualToLogicalMapBuffer + index );
187
188       const CharacterDirection direction0 = *( modelCharacterDirections + logicalPosition0 );
189       const CharacterDirection direction1 = *( modelCharacterDirections + logicalPosition1 );
190
191       if( direction0 == direction1 )
192       {
193         // Both glyphs have the same direction.
194         if( direction0 )
195         {
196           *( modelVisualToLogicalCursorMap + index ) = logicalPosition0;
197         }
198         else
199         {
200           *( modelVisualToLogicalCursorMap + index ) = logicalPosition1;
201         }
202       }
203       else
204       {
205         if( isRightToLeftParagraph )
206         {
207           if( direction1 )
208           {
209             *( modelVisualToLogicalCursorMap + index ) = logicalPosition1 + 1u;
210           }
211           else
212           {
213             *( modelVisualToLogicalCursorMap + index ) = logicalPosition0;
214           }
215         }
216         else
217         {
218           if( direction0 )
219           {
220             *( modelVisualToLogicalCursorMap + index ) = logicalPosition1;
221           }
222           else
223           {
224             *( modelVisualToLogicalCursorMap + index ) = logicalPosition0 + 1u;
225           }
226         }
227       }
228     }
229
230     if( bidirectionalParagraph &&
231         ( bidirectionalParagraph->characterRun.characterIndex + bidirectionalParagraph->characterRun.numberOfCharacters == index ) )
232     {
233       isRightToLeftParagraph = false;
234       ++bidirectionalParagraphIndex;
235       if( bidirectionalParagraphIndex < numberOfBidirectionalParagraphs )
236       {
237         bidirectionalParagraph = bidirectionalParagraphInfoBuffer + bidirectionalParagraphIndex;
238       }
239       else
240       {
241         bidirectionalParagraph = NULL;
242       }
243     }
244   }
245 }
246
247 CharacterIndex LogicalModel::GetLogicalCharacterIndex( CharacterIndex visualCharacterIndex ) const
248 {
249   if( 0u == mVisualToLogicalMap.Count() )
250   {
251     // If there is no visual to logical info is because the whole text is left to right.
252     // Return the identity.
253     return visualCharacterIndex;
254   }
255
256   return *( mVisualToLogicalMap.Begin() + visualCharacterIndex );
257 }
258
259 void LogicalModel::UpdateTextStyleRuns( CharacterIndex index, int numberOfCharacters )
260 {
261   const Length totalNumberOfCharacters = mText.Count();
262
263   // Process the color runs.
264   Vector<ColorRun> removedColorRuns;
265   UpdateCharacterRuns<ColorRun>( index,
266                                  numberOfCharacters,
267                                  totalNumberOfCharacters,
268                                  mColorRuns,
269                                  removedColorRuns );
270
271   // Process the font description runs.
272   Vector<FontDescriptionRun> removedFontDescriptionRuns;
273   UpdateCharacterRuns<FontDescriptionRun>( index,
274                                            numberOfCharacters,
275                                            totalNumberOfCharacters,
276                                            mFontDescriptionRuns,
277                                            removedFontDescriptionRuns );
278
279   // Free memory allocated for the font family name.
280   FreeFontFamilyNames( removedFontDescriptionRuns );
281 }
282
283 void LogicalModel::RetrieveStyle( CharacterIndex index, InputStyle& style )
284 {
285   unsigned int runIndex = 0u;
286
287   // Set the text color.
288   bool colorOverriden = false;
289   unsigned int colorIndex = 0u;
290   const ColorRun* const colorRunsBuffer = mColorRuns.Begin();
291   for( Vector<ColorRun>::ConstIterator it = colorRunsBuffer,
292          endIt = mColorRuns.End();
293        it != endIt;
294        ++it, ++runIndex )
295   {
296     const ColorRun& colorRun = *it;
297
298     if( ( colorRun.characterRun.characterIndex <= index ) &&
299         ( index < colorRun.characterRun.characterIndex + colorRun.characterRun.numberOfCharacters ) )
300     {
301       colorIndex = runIndex;
302       colorOverriden = true;
303     }
304   }
305
306   // Set the text's color if it's overriden.
307   if( colorOverriden )
308   {
309     style.textColor = ( *( colorRunsBuffer + colorIndex ) ).color;
310     style.isDefaultColor = false;
311   }
312
313   // Reset the run index.
314   runIndex = 0u;
315
316   // Set the font's parameters.
317   bool nameOverriden = false;
318   bool weightOverriden = false;
319   bool widthOverriden = false;
320   bool slantOverriden = false;
321   bool sizeOverriden = false;
322   unsigned int nameIndex = 0u;
323   unsigned int weightIndex = 0u;
324   unsigned int widthIndex = 0u;
325   unsigned int slantIndex = 0u;
326   unsigned int sizeIndex = 0u;
327   const FontDescriptionRun* const fontDescriptionRunsBuffer = mFontDescriptionRuns.Begin();
328   for( Vector<FontDescriptionRun>::ConstIterator it = fontDescriptionRunsBuffer,
329          endIt = mFontDescriptionRuns.End();
330        it != endIt;
331        ++it, ++runIndex )
332   {
333     const FontDescriptionRun& fontDescriptionRun = *it;
334
335     if( ( fontDescriptionRun.characterRun.characterIndex <= index ) &&
336         ( index < fontDescriptionRun.characterRun.characterIndex + fontDescriptionRun.characterRun.numberOfCharacters ) )
337     {
338       if( fontDescriptionRun.familyDefined )
339       {
340         nameIndex = runIndex;
341         nameOverriden = true;
342       }
343
344       if( fontDescriptionRun.weightDefined )
345       {
346         weightIndex = runIndex;
347         weightOverriden = true;
348       }
349
350       if( fontDescriptionRun.widthDefined )
351       {
352         widthIndex = runIndex;
353         widthOverriden = true;
354       }
355
356       if( fontDescriptionRun.slantDefined )
357       {
358         slantIndex = runIndex;
359         slantOverriden = true;
360       }
361
362       if( fontDescriptionRun.sizeDefined )
363       {
364         sizeIndex = runIndex;
365         sizeOverriden = true;
366       }
367     }
368   }
369
370   // Set the font's family name if it's overriden.
371   if( nameOverriden )
372   {
373     const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + nameIndex );
374
375     style.familyName = std::string( fontDescriptionRun.familyName, fontDescriptionRun.familyLength );
376     style.familyDefined = true;
377   }
378
379   // Set the font's weight if it's overriden.
380   if( weightOverriden )
381   {
382     const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + weightIndex );
383
384     style.weight = fontDescriptionRun.weight;
385     style.weightDefined = true;
386   }
387
388   // Set the font's width if it's overriden.
389   if( widthOverriden )
390   {
391     const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + widthIndex );
392
393     style.width = fontDescriptionRun.width;
394     style.widthDefined = true;
395   }
396
397   // Set the font's slant if it's overriden.
398   if( slantOverriden )
399   {
400     const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + slantIndex );
401
402     style.slant = fontDescriptionRun.slant;
403     style.slantDefined = true;
404   }
405
406   // Set the font's size if it's overriden.
407   if( sizeOverriden )
408   {
409     const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + sizeIndex );
410
411     style.size = static_cast<float>( fontDescriptionRun.size ) / 64.f;
412     style.sizeDefined = true;
413   }
414 }
415
416 void LogicalModel::ClearFontDescriptionRuns()
417 {
418   FreeFontFamilyNames( mFontDescriptionRuns );
419 }
420
421 void LogicalModel::CreateParagraphInfo( CharacterIndex startIndex,
422                                         Length numberOfCharacters )
423 {
424   const Length totalNumberOfCharacters = mLineBreakInfo.Count();
425
426   // Count the number of LINE_MUST_BREAK to reserve some space for the vector of paragraph's info.
427   Vector<CharacterIndex> paragraphs;
428   paragraphs.Reserve( numberOfCharacters );
429   const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = mLineBreakInfo.Begin();
430   const CharacterIndex lastCharacterIndexPlusOne = startIndex + numberOfCharacters;
431   for( Length index = startIndex; index < lastCharacterIndexPlusOne; ++index )
432   {
433     if( TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + index ) )
434     {
435       paragraphs.PushBack( index );
436     }
437   }
438
439   // Whether the current paragraphs are updated or set from scratch.
440   const bool updateCurrentParagraphs = numberOfCharacters < totalNumberOfCharacters;
441
442   // Reserve space for current paragraphs plus new ones.
443   const Length numberOfNewParagraphs = paragraphs.Count();
444   const Length totalNumberOfParagraphs = mParagraphInfo.Count() + numberOfNewParagraphs;
445   mParagraphInfo.Resize( totalNumberOfParagraphs );
446
447   ParagraphRun* paragraphInfoBuffer = NULL;
448   Vector<ParagraphRun> newParagraphs;
449
450   if( updateCurrentParagraphs )
451   {
452     newParagraphs.Resize( numberOfNewParagraphs );
453     paragraphInfoBuffer = newParagraphs.Begin();
454   }
455   else
456   {
457     paragraphInfoBuffer = mParagraphInfo.Begin();
458   }
459
460   // Find where to insert the new paragraphs.
461   ParagraphRunIndex paragraphIndex = 0u;
462   CharacterIndex firstIndex = startIndex;
463
464   if( updateCurrentParagraphs )
465   {
466     for( Vector<ParagraphRun>::ConstIterator it = mParagraphInfo.Begin(),
467            endIt = mParagraphInfo.Begin() + totalNumberOfParagraphs - numberOfNewParagraphs;
468          it != endIt;
469          ++it )
470     {
471       const ParagraphRun& paragraph( *it );
472
473       if( startIndex < paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
474       {
475         firstIndex = paragraph.characterRun.characterIndex;
476         break;
477       }
478
479       ++paragraphIndex;
480     }
481   }
482
483   // Create the paragraph info.
484   ParagraphRunIndex newParagraphIndex = 0u;
485   for( Vector<CharacterIndex>::ConstIterator it = paragraphs.Begin(),
486          endIt = paragraphs.End();
487        it != endIt;
488        ++it, ++newParagraphIndex )
489   {
490     const CharacterIndex index = *it;
491
492     ParagraphRun& paragraph = *( paragraphInfoBuffer + newParagraphIndex );
493     paragraph.characterRun.characterIndex = firstIndex;
494     paragraph.characterRun.numberOfCharacters = 1u + index - firstIndex;
495
496     firstIndex += paragraph.characterRun.numberOfCharacters;
497   }
498
499
500   // Insert the new paragraphs.
501   if( updateCurrentParagraphs )
502   {
503     mParagraphInfo.Insert( mParagraphInfo.Begin() + paragraphIndex,
504                            newParagraphs.Begin(),
505                            newParagraphs.End() );
506
507     mParagraphInfo.Resize( totalNumberOfParagraphs );
508
509     // Update the next paragraph indices.
510     for( Vector<ParagraphRun>::Iterator it = mParagraphInfo.Begin() + paragraphIndex + newParagraphs.Count(),
511            endIt = mParagraphInfo.End();
512          it != endIt;
513          ++it )
514     {
515       ParagraphRun& paragraph( *it );
516
517       paragraph.characterRun.characterIndex += numberOfCharacters;
518     }
519   }
520 }
521
522 void LogicalModel::FindParagraphs( CharacterIndex index,
523                                    Length numberOfCharacters,
524                                    Vector<ParagraphRunIndex>& paragraphs )
525 {
526   // Reserve som space for the paragraph indices.
527   paragraphs.Reserve( mParagraphInfo.Count() );
528
529   // Traverse the paragraphs to find which ones contain the given characters.
530   ParagraphRunIndex paragraphIndex = 0u;
531   for( Vector<ParagraphRun>::ConstIterator it = mParagraphInfo.Begin(),
532          endIt = mParagraphInfo.End();
533        it != endIt;
534        ++it, ++paragraphIndex )
535   {
536     const ParagraphRun& paragraph( *it );
537
538     if( ( paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters > index ) &&
539         ( paragraph.characterRun.characterIndex < index + numberOfCharacters ) )
540     {
541       paragraphs.PushBack( paragraphIndex );
542     }
543   }
544 }
545
546 LogicalModel::~LogicalModel()
547 {
548   ClearFontDescriptionRuns();
549 }
550
551 LogicalModel::LogicalModel()
552 {
553 }
554
555 } // namespace Text
556
557 } // namespace Toolkit
558
559 } // namespace Dali