239a2f8897a870950d77598da18a36205e0f60ae
[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   }
311
312   // Reset the run index.
313   runIndex = 0u;
314
315   // Set the font's parameters.
316   bool nameOverriden = false;
317   bool weightOverriden = false;
318   bool widthOverriden = false;
319   bool slantOverriden = false;
320   bool sizeOverriden = false;
321   unsigned int nameIndex = 0u;
322   unsigned int weightIndex = 0u;
323   unsigned int widthIndex = 0u;
324   unsigned int slantIndex = 0u;
325   unsigned int sizeIndex = 0u;
326   const FontDescriptionRun* const fontDescriptionRunsBuffer = mFontDescriptionRuns.Begin();
327   for( Vector<FontDescriptionRun>::ConstIterator it = fontDescriptionRunsBuffer,
328          endIt = mFontDescriptionRuns.End();
329        it != endIt;
330        ++it, ++runIndex )
331   {
332     const FontDescriptionRun& fontDescriptionRun = *it;
333
334     if( ( fontDescriptionRun.characterRun.characterIndex <= index ) &&
335         ( index < fontDescriptionRun.characterRun.characterIndex + fontDescriptionRun.characterRun.numberOfCharacters ) )
336     {
337       if( fontDescriptionRun.familyDefined )
338       {
339         nameIndex = runIndex;
340         nameOverriden = true;
341       }
342
343       if( fontDescriptionRun.weightDefined )
344       {
345         weightIndex = runIndex;
346         weightOverriden = true;
347       }
348
349       if( fontDescriptionRun.widthDefined )
350       {
351         widthIndex = runIndex;
352         widthOverriden = true;
353       }
354
355       if( fontDescriptionRun.slantDefined )
356       {
357         slantIndex = runIndex;
358         slantOverriden = true;
359       }
360
361       if( fontDescriptionRun.sizeDefined )
362       {
363         sizeIndex = runIndex;
364         sizeOverriden = true;
365       }
366     }
367   }
368
369   // Set the font's family name if it's overriden.
370   if( nameOverriden )
371   {
372     const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + nameIndex );
373
374     style.familyName = std::string( fontDescriptionRun.familyName, fontDescriptionRun.familyLength );
375     style.familyDefined = true;
376   }
377
378   // Set the font's weight if it's overriden.
379   if( weightOverriden )
380   {
381     const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + weightIndex );
382
383     style.weight = fontDescriptionRun.weight;
384     style.weightDefined = true;
385   }
386
387   // Set the font's width if it's overriden.
388   if( widthOverriden )
389   {
390     const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + widthIndex );
391
392     style.width = fontDescriptionRun.width;
393     style.widthDefined = true;
394   }
395
396   // Set the font's slant if it's overriden.
397   if( slantOverriden )
398   {
399     const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + slantIndex );
400
401     style.slant = fontDescriptionRun.slant;
402     style.slantDefined = true;
403   }
404
405   // Set the font's size if it's overriden.
406   if( sizeOverriden )
407   {
408     const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + sizeIndex );
409
410     style.size = static_cast<float>( fontDescriptionRun.size ) / 64.f;
411     style.sizeDefined = true;
412   }
413
414   // Reset the run index.
415   runIndex = 0u;
416 }
417
418 void LogicalModel::ClearFontDescriptionRuns()
419 {
420   FreeFontFamilyNames( mFontDescriptionRuns );
421 }
422
423 void LogicalModel::CreateParagraphInfo( CharacterIndex startIndex,
424                                         Length numberOfCharacters )
425 {
426   const Length totalNumberOfCharacters = mLineBreakInfo.Count();
427
428   // Count the number of LINE_MUST_BREAK to reserve some space for the vector of paragraph's info.
429   Vector<CharacterIndex> paragraphs;
430   paragraphs.Reserve( numberOfCharacters );
431   const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = mLineBreakInfo.Begin();
432   const CharacterIndex lastCharacterIndexPlusOne = startIndex + numberOfCharacters;
433   for( Length index = startIndex; index < lastCharacterIndexPlusOne; ++index )
434   {
435     if( TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + index ) )
436     {
437       paragraphs.PushBack( index );
438     }
439   }
440
441   // Whether the current paragraphs are updated or set from scratch.
442   const bool updateCurrentParagraphs = numberOfCharacters < totalNumberOfCharacters;
443
444   // Reserve space for current paragraphs plus new ones.
445   const Length numberOfNewParagraphs = paragraphs.Count();
446   const Length totalNumberOfParagraphs = mParagraphInfo.Count() + numberOfNewParagraphs;
447   mParagraphInfo.Resize( totalNumberOfParagraphs );
448
449   ParagraphRun* paragraphInfoBuffer = NULL;
450   Vector<ParagraphRun> newParagraphs;
451
452   if( updateCurrentParagraphs )
453   {
454     newParagraphs.Resize( numberOfNewParagraphs );
455     paragraphInfoBuffer = newParagraphs.Begin();
456   }
457   else
458   {
459     paragraphInfoBuffer = mParagraphInfo.Begin();
460   }
461
462   // Find where to insert the new paragraphs.
463   ParagraphRunIndex paragraphIndex = 0u;
464   CharacterIndex firstIndex = startIndex;
465
466   if( updateCurrentParagraphs )
467   {
468     for( Vector<ParagraphRun>::ConstIterator it = mParagraphInfo.Begin(),
469            endIt = mParagraphInfo.Begin() + totalNumberOfParagraphs - numberOfNewParagraphs;
470          it != endIt;
471          ++it )
472     {
473       const ParagraphRun& paragraph( *it );
474
475       if( startIndex < paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
476       {
477         firstIndex = paragraph.characterRun.characterIndex;
478         break;
479       }
480
481       ++paragraphIndex;
482     }
483   }
484
485   // Create the paragraph info.
486   ParagraphRunIndex newParagraphIndex = 0u;
487   for( Vector<CharacterIndex>::ConstIterator it = paragraphs.Begin(),
488          endIt = paragraphs.End();
489        it != endIt;
490        ++it, ++newParagraphIndex )
491   {
492     const CharacterIndex index = *it;
493
494     ParagraphRun& paragraph = *( paragraphInfoBuffer + newParagraphIndex );
495     paragraph.characterRun.characterIndex = firstIndex;
496     paragraph.characterRun.numberOfCharacters = 1u + index - firstIndex;
497
498     firstIndex += paragraph.characterRun.numberOfCharacters;
499   }
500
501
502   // Insert the new paragraphs.
503   if( updateCurrentParagraphs )
504   {
505     mParagraphInfo.Insert( mParagraphInfo.Begin() + paragraphIndex,
506                            newParagraphs.Begin(),
507                            newParagraphs.End() );
508
509     mParagraphInfo.Resize( totalNumberOfParagraphs );
510
511     // Update the next paragraph indices.
512     for( Vector<ParagraphRun>::Iterator it = mParagraphInfo.Begin() + paragraphIndex + newParagraphs.Count(),
513            endIt = mParagraphInfo.End();
514          it != endIt;
515          ++it )
516     {
517       ParagraphRun& paragraph( *it );
518
519       paragraph.characterRun.characterIndex += numberOfCharacters;
520     }
521   }
522 }
523
524 void LogicalModel::FindParagraphs( CharacterIndex index,
525                                    Length numberOfCharacters,
526                                    Vector<ParagraphRunIndex>& paragraphs )
527 {
528   // Reserve som space for the paragraph indices.
529   paragraphs.Reserve( mParagraphInfo.Count() );
530
531   // Traverse the paragraphs to find which ones contain the given characters.
532   ParagraphRunIndex paragraphIndex = 0u;
533   for( Vector<ParagraphRun>::ConstIterator it = mParagraphInfo.Begin(),
534          endIt = mParagraphInfo.End();
535        it != endIt;
536        ++it, ++paragraphIndex )
537   {
538     const ParagraphRun& paragraph( *it );
539
540     if( ( paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters > index ) &&
541         ( paragraph.characterRun.characterIndex < index + numberOfCharacters ) )
542     {
543       paragraphs.PushBack( paragraphIndex );
544     }
545   }
546 }
547
548 LogicalModel::~LogicalModel()
549 {
550   ClearFontDescriptionRuns();
551 }
552
553 LogicalModel::LogicalModel()
554 {
555 }
556
557 } // namespace Text
558
559 } // namespace Toolkit
560
561 } // namespace Dali