2faca97dea759cee1646eda91adb1f3435035111
[platform/core/uifw/dali-toolkit.git] / 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 // EXTERNAL INCLUDES
22 #include <memory.h>
23
24 namespace Dali
25 {
26
27 namespace Toolkit
28 {
29
30 namespace Text
31 {
32
33 LogicalModelPtr LogicalModel::New()
34 {
35   return LogicalModelPtr( new LogicalModel() );
36 }
37
38 void LogicalModel::SetText( const Character* const text,
39                             Length numberOfCharacters )
40 {
41   if( 0u == numberOfCharacters )
42   {
43     mText.Clear();
44   }
45   else
46   {
47     mText.Resize( numberOfCharacters );
48     memcpy( mText.Begin(), text, numberOfCharacters * sizeof( Character ) );
49   }
50 }
51
52 Length LogicalModel::GetNumberOfCharacters() const
53 {
54   return mText.Count();
55 }
56
57 void LogicalModel::GetText( Character* text,
58                             CharacterIndex characterIndex,
59                             Length numberOfCharacters ) const
60 {
61   memcpy( text, mText.Begin() + characterIndex, numberOfCharacters * sizeof( Character ) );
62 }
63
64 Character LogicalModel::GetCharacter( CharacterIndex characterIndex ) const
65 {
66   return mText[characterIndex];
67 }
68
69 void LogicalModel::ReplaceText( CharacterIndex characterIndex,
70                                 Length numberOfCharactersToRemove,
71                                 const Character* const text,
72                                 Length numberOfCharactersToInsert )
73 {
74 }
75
76 void LogicalModel::SetScripts( const ScriptRun* const scripts,
77                                Length numberOfRuns )
78 {
79   if( 0u == numberOfRuns )
80   {
81     mScriptRuns.Clear();
82   }
83   else
84   {
85     mScriptRuns.Resize( numberOfRuns );
86     memcpy( mScriptRuns.Begin(), scripts, numberOfRuns * sizeof( ScriptRun ) );
87   }
88 }
89
90 void LogicalModel::GetNumberOfScriptRuns( CharacterIndex characterIndex,
91                                           Length numberOfCharacters,
92                                           ScriptRunIndex& firstScriptRun,
93                                           Length& numberOfScriptRuns ) const
94 {
95   // Initialize the number of scripts and the index to the first script.
96   firstScriptRun = 0u;
97   numberOfScriptRuns = 0;
98   bool firstScriptFound = false;
99
100   const CharacterIndex lastCharacterIndex = characterIndex + numberOfCharacters;
101
102   // Traverse the scripts and count those scripts within the range of characters.
103   for( Vector<ScriptRun>::ConstIterator it = mScriptRuns.Begin(),
104          endIt = mScriptRuns.End();
105        it != endIt;
106        ++it )
107   {
108     const ScriptRun& script = *it;
109
110     if( ( script.characterRun.characterIndex + script.characterRun.numberOfCharacters > characterIndex ) &&
111         ( lastCharacterIndex > script.characterRun.characterIndex ) )
112     {
113       firstScriptFound = true;
114       ++numberOfScriptRuns;
115     }
116     else if( lastCharacterIndex <= script.characterRun.characterIndex )
117     {
118       // nothing else to do.
119       break;
120     }
121
122     if( !firstScriptFound )
123     {
124       ++firstScriptRun;
125     }
126   }
127 }
128
129 void LogicalModel::GetScriptRuns( ScriptRun* scriptRuns,
130                                   CharacterIndex characterIndex,
131                                   Length numberOfCharacters ) const
132 {
133   ScriptRunIndex firstScriptRun = 0u;
134   Length numberOfScriptRuns = 0u;
135
136   GetNumberOfScriptRuns( characterIndex,
137                          numberOfCharacters,
138                          firstScriptRun,
139                          numberOfScriptRuns );
140
141   memcpy( scriptRuns, mScriptRuns.Begin() + firstScriptRun, numberOfScriptRuns * sizeof( ScriptRun ) );
142 }
143
144 Script LogicalModel::GetScript( CharacterIndex characterIndex ) const
145 {
146   // If this operation is too slow, consider a binary search.
147
148   for( Length index = 0u, length = mScriptRuns.Count(); index < length; ++index )
149   {
150     const ScriptRun* const scriptRun = mScriptRuns.Begin() + index;
151
152     if( ( scriptRun->characterRun.characterIndex <= characterIndex ) &&
153         ( characterIndex < scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters ) )
154     {
155       return scriptRun->script;
156     }
157   }
158
159   return TextAbstraction::UNKNOWN;
160 }
161
162 void LogicalModel::ReplaceScripts( CharacterIndex characterIndex,
163                                    Length numberOfCharactersToRemove,
164                                    const ScriptRun* const scriptRuns,
165                                    Length numberOfCharactersToInsert )
166 {
167 }
168
169 void LogicalModel::SetFonts( const FontRun* const fonts,
170                              Length numberOfRuns )
171 {
172   if( 0u == numberOfRuns )
173   {
174     mFontRuns.Clear();
175   }
176   else
177   {
178     mFontRuns.Resize( numberOfRuns );
179     memcpy( mFontRuns.Begin(), fonts, numberOfRuns * sizeof( FontRun ) );
180   }
181 }
182
183 void LogicalModel::GetNumberOfFontRuns( CharacterIndex characterIndex,
184                                         Length numberOfCharacters,
185                                         FontRunIndex& firstFontRun,
186                                         Length& numberOfFontRuns ) const
187 {
188   // Initialize the number of fonts and the index to the first font.
189   firstFontRun = 0u;
190   numberOfFontRuns = 0;
191   bool firstFontFound = false;
192
193   const CharacterIndex lastCharacterIndex = characterIndex + numberOfCharacters;
194
195   // Traverse the fonts and count those fonts within the range of characters.
196   for( Vector<FontRun>::ConstIterator it = mFontRuns.Begin(),
197          endIt = mFontRuns.End();
198        it != endIt;
199        ++it )
200   {
201     const FontRun& font = *it;
202
203     if( ( font.characterRun.characterIndex + font.characterRun.numberOfCharacters > characterIndex ) &&
204         ( characterIndex + numberOfCharacters > font.characterRun.characterIndex ) )
205     {
206       firstFontFound = true;
207       ++numberOfFontRuns;
208     }
209     else if( lastCharacterIndex <= font.characterRun.characterIndex )
210     {
211       // nothing else to do.
212       break;
213     }
214
215     if( !firstFontFound )
216     {
217       ++firstFontRun;
218     }
219   }
220 }
221
222 void LogicalModel::GetFontRuns( FontRun* fontRuns,
223                                 CharacterIndex characterIndex,
224                                 Length numberOfCharacters ) const
225 {
226   FontRunIndex firstFontRun = 0u;
227   Length numberOfFontRuns = 0u;
228
229   GetNumberOfFontRuns( characterIndex,
230                        numberOfCharacters,
231                        firstFontRun,
232                        numberOfFontRuns );
233
234   memcpy( fontRuns, mFontRuns.Begin() + firstFontRun, numberOfFontRuns * sizeof( FontRun ) );
235 }
236
237 FontId LogicalModel::GetFont( CharacterIndex characterIndex ) const
238 {
239   for( Length index = 0u, length = mFontRuns.Count(); index < length; ++index )
240   {
241     const FontRun* const fontRun = mFontRuns.Begin() + index;
242
243     if( ( fontRun->characterRun.characterIndex <= characterIndex ) &&
244         ( characterIndex < fontRun->characterRun.characterIndex + fontRun->characterRun.numberOfCharacters ) )
245     {
246       return fontRun->fontId;
247     }
248   }
249
250   return 0u;
251 }
252
253 void LogicalModel::ReplaceFonts( CharacterIndex characterIndex,
254                                  Length numberOfCharactersToRemove,
255                                  const FontRun* const fontRuns,
256                                  Length numberOfCharactersToInsert )
257 {
258 }
259
260 void LogicalModel::SetLineBreakInfo( const LineBreakInfo* const lineBreakInfo,
261                                      Length length )
262 {
263   if( 0u == length )
264   {
265     mLineBreakInfo.Clear();
266   }
267   else
268   {
269     mLineBreakInfo.Resize( length );
270     memcpy( mLineBreakInfo.Begin(), lineBreakInfo, length * sizeof( LineBreakInfo ) );
271   }
272 }
273
274 void LogicalModel::GetLineBreakInfo( LineBreakInfo* lineBreakInfo,
275                                      CharacterIndex characterIndex,
276                                      Length numberOfItems ) const
277 {
278   memcpy( lineBreakInfo, mLineBreakInfo.Begin() + characterIndex, numberOfItems * sizeof( LineBreakInfo ) );
279 }
280
281 LineBreakInfo LogicalModel::GetLineBreakInfo( CharacterIndex characterIndex ) const
282 {
283   return *( mLineBreakInfo.Begin() + characterIndex );
284 }
285
286 void LogicalModel::ReplaceLineBreakInfo( CharacterIndex characterIndex,
287                                          Length numberOfItemsToRemove,
288                                          const LineBreakInfo* const lineBreakInfo,
289                                          Length numberOfItemsToInsert )
290 {
291 }
292
293 void LogicalModel::SetWordBreakInfo( const WordBreakInfo* const wordBreakInfo,
294                                      Length length )
295 {
296   if( 0u == length )
297   {
298     mWordBreakInfo.Clear();
299   }
300   else
301   {
302     mWordBreakInfo.Resize( length );
303     memcpy( mWordBreakInfo.Begin(), wordBreakInfo, length * sizeof( WordBreakInfo ) );
304   }
305 }
306
307 void LogicalModel::GetWordBreakInfo( WordBreakInfo* wordBreakInfo,
308                                      CharacterIndex characterIndex,
309                                      Length numberOfItems ) const
310 {
311   memcpy( wordBreakInfo, mWordBreakInfo.Begin() + characterIndex, numberOfItems * sizeof( WordBreakInfo ) );
312 }
313
314 WordBreakInfo LogicalModel::GetWordBreakInfo( CharacterIndex characterIndex ) const
315 {
316   return *( mWordBreakInfo.Begin() + characterIndex );
317 }
318
319 void LogicalModel::ReplaceWordBreakInfo( CharacterIndex characterIndex,
320                                          Length numberOfItemsToRemove,
321                                          const WordBreakInfo* const wordBreakInfo,
322                                          Length numberOfItemsToInsert )
323 {
324 }
325
326 void LogicalModel::SetBidirectionalInfo( const BidirectionalParagraphInfoRun* const bidirectionalInfo,
327                                          Length numberOfRuns )
328 {
329   if( 0u == numberOfRuns )
330   {
331     mBidirectionalParagraphInfo.Clear();
332   }
333   else
334   {
335     mBidirectionalParagraphInfo.Resize( numberOfRuns );
336     memcpy( mBidirectionalParagraphInfo.Begin(), bidirectionalInfo, numberOfRuns * sizeof( BidirectionalParagraphInfoRun ) );
337   }
338 }
339
340 void LogicalModel::GetNumberOfBidirectionalInfoRuns( CharacterIndex characterIndex,
341                                                      Length numberOfCharacters,
342                                                      BidirectionalRunIndex& firstBidirectionalRun,
343                                                      Length& numberOfFontRuns  ) const
344 {
345   // Initialize the number of bidi paragraphs and the index to the first paragraph.
346   firstBidirectionalRun = 0u;
347   numberOfFontRuns = 0;
348   bool firstParagraphFound = false;
349
350   // Traverse the bidirectional paragraph info and count those bidi paragraphs within the range of characters.
351   for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = mBidirectionalParagraphInfo.Begin(),
352          endIt = mBidirectionalParagraphInfo.End();
353        it != endIt;
354        ++it )
355   {
356     const BidirectionalParagraphInfoRun& bidi = *it;
357
358     if( ( bidi.characterRun.characterIndex + bidi.characterRun.numberOfCharacters > characterIndex ) &&
359         ( characterIndex + numberOfCharacters > bidi.characterRun.characterIndex ) )
360     {
361       firstParagraphFound = true;
362       ++numberOfFontRuns;
363     }
364
365     if( !firstParagraphFound )
366     {
367       ++firstBidirectionalRun;
368     }
369   }
370 }
371
372 void LogicalModel::GetBidirectionalInfo( BidirectionalParagraphInfoRun* bidirectionalInfo,
373                                          CharacterIndex characterIndex,
374                                          Length numberOfCharacters ) const
375 {
376   BidirectionalRunIndex firstBidirectionalRun = 0u;
377   Length numberOfFontRuns = 0u;
378
379   GetNumberOfBidirectionalInfoRuns( characterIndex,
380                                     numberOfCharacters,
381                                     firstBidirectionalRun,
382                                     numberOfFontRuns );
383
384   memcpy( bidirectionalInfo, mBidirectionalParagraphInfo.Begin() + firstBidirectionalRun, numberOfFontRuns * sizeof( BidirectionalParagraphInfoRun ) );
385 }
386
387 void ReplaceBidirectionalInfo( CharacterIndex characterIndex,
388                                Length numberOfCharactersToRemove,
389                                const BidirectionalParagraphInfoRun* const bidirectionalInfo,
390                                Length numberOfCharactersToInsert )
391 {
392 }
393
394 void LogicalModel::SetCharacterDirections( const CharacterDirection* const directions,
395                                            Length numberOfCharacters )
396 {
397   if( 0u == numberOfCharacters )
398   {
399     mCharacterDirections.Clear();
400   }
401   else
402   {
403     mCharacterDirections.Resize( numberOfCharacters );
404     memcpy( mCharacterDirections.Begin(), directions, numberOfCharacters * sizeof( CharacterDirection ) );
405   }
406 }
407
408 void LogicalModel::GetCharacterDirections( CharacterDirection* directions,
409                                            CharacterIndex characterIndex,
410                                            Length numberOfCharacters ) const
411 {
412   if( 0u == mCharacterDirections.Count() )
413   {
414     // Nothing to retrieve if the model has no right to left characters.
415     return;
416   }
417
418   memcpy( directions, mCharacterDirections.Begin() + characterIndex, numberOfCharacters * sizeof( CharacterDirection ) );
419 }
420
421 CharacterDirection LogicalModel::GetCharacterDirection( CharacterIndex characterIndex ) const
422 {
423   if( characterIndex >= mCharacterDirections.Count() )
424   {
425     // The model has no right to left characters, so the vector of directions is void.
426     return false;
427   }
428
429   return *( mCharacterDirections.Begin() + characterIndex );
430 }
431
432 void LogicalModel::SetVisualToLogicalMap( const BidirectionalLineInfoRun* const bidirectionalInfo,
433                                           Length numberOfRuns )
434 {
435   if( 0u == numberOfRuns )
436   {
437     mVisualToLogicalMap.Clear();
438     mLogicalToVisualMap.Clear();
439     mVisualToLogicalCursorMap.Clear();
440   }
441   else
442   {
443     const Length numberOfCharacters = mText.Count();
444     mVisualToLogicalMap.Resize( numberOfCharacters );
445     mLogicalToVisualMap.Resize( numberOfCharacters );
446
447     const Length numberOfCharactersPlus = numberOfCharacters + 1u;
448     mVisualToLogicalCursorMap.Resize( numberOfCharactersPlus );
449
450     CharacterIndex* modelVisualToLogicalMapBuffer = mVisualToLogicalMap.Begin();
451     CharacterIndex* modelLogicalToVisualMapBuffer = mLogicalToVisualMap.Begin();
452
453     CharacterIndex* modelVisualToLogicalCursorMap = mVisualToLogicalCursorMap.Begin();
454
455     CharacterIndex lastIndex = 0u;
456     for( unsigned int bidiIndex = 0u; bidiIndex < numberOfRuns; ++bidiIndex )
457     {
458       const BidirectionalLineInfoRun& bidiLineInfo = *( bidirectionalInfo + bidiIndex );
459
460       if( lastIndex < bidiLineInfo.characterRun.characterIndex )
461       {
462         // Fill with the identity.
463         for( ; lastIndex < bidiLineInfo.characterRun.characterIndex; ++lastIndex )
464         {
465           *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex;
466         }
467       }
468
469       // Fill the conversion table of the run.
470       for( CharacterIndex index = 0u;
471            index < bidiLineInfo.characterRun.numberOfCharacters;
472            ++index, ++lastIndex )
473       {
474         *( modelVisualToLogicalMapBuffer + lastIndex ) = bidiLineInfo.characterRun.characterIndex + *( bidiLineInfo.visualToLogicalMap + index );
475       }
476     }
477
478     // Complete with the identity if there are some left to right characters after the last right to left.
479     for( ; lastIndex < numberOfCharacters; ++lastIndex )
480     {
481       *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex;
482     }
483
484     // Sets the logical to visual conversion map.
485     for( CharacterIndex index = 0u; index < numberOfCharacters; ++index )
486     {
487       *( modelLogicalToVisualMapBuffer + *( modelVisualToLogicalMapBuffer + index ) ) = index;
488     }
489
490     // Sets the visual to logical conversion map for cursor positions.
491
492     const Length numberOfBidirectionalParagraphs = mBidirectionalParagraphInfo.Count();
493     BidirectionalParagraphInfoRun* bidirectionalParagraphInfoBuffer = mBidirectionalParagraphInfo.Begin();
494     BidirectionalParagraphInfoRun* bidirectionalParagraph = bidirectionalParagraphInfoBuffer;
495
496     const CharacterDirection* const modelCharacterDirections = mCharacterDirections.Begin();
497
498     Length bidirectionalParagraphIndex = 0u;
499     bool isRightToLeftParagraph = false;
500     for( CharacterIndex index = 0u; index < numberOfCharactersPlus; ++index )
501     {
502       if( bidirectionalParagraph &&
503           ( bidirectionalParagraph->characterRun.characterIndex == index ) )
504       {
505         isRightToLeftParagraph = *( modelCharacterDirections + index );
506       }
507
508       if( 0u == index )
509       {
510         if( isRightToLeftParagraph )
511         {
512           *( modelVisualToLogicalCursorMap + index ) = numberOfCharacters;
513         }
514         else // else logical position is zero.
515         {
516           *( modelVisualToLogicalCursorMap + index ) = 0u;
517         }
518       }
519       else if( numberOfCharacters == index )
520       {
521         if( isRightToLeftParagraph )
522         {
523           *( modelVisualToLogicalCursorMap + index ) = 0u;
524         }
525         else // else logical position is the number of characters.
526         {
527           *( modelVisualToLogicalCursorMap + index ) = numberOfCharacters;
528         }
529       }
530       else
531       {
532         // Get the character indexed by  index - 1 and index
533         // and calculate the logical position according the directions of
534         // both characters and the direction of the paragraph.
535
536         const CharacterIndex previousIndex = index - 1u;
537         const CharacterIndex logicalPosition0 = *( modelVisualToLogicalMapBuffer + previousIndex );
538         const CharacterIndex logicalPosition1 = *( modelVisualToLogicalMapBuffer + index );
539
540         const CharacterDirection direction0 = *( modelCharacterDirections + logicalPosition0 );
541         const CharacterDirection direction1 = *( modelCharacterDirections + logicalPosition1 );
542
543         if( direction0 == direction1 )
544         {
545           // Both glyphs have the same direction.
546           if( direction0 )
547           {
548             *( modelVisualToLogicalCursorMap + index ) = logicalPosition0;
549           }
550           else
551           {
552             *( modelVisualToLogicalCursorMap + index ) = logicalPosition1;
553           }
554         }
555         else
556         {
557           if( isRightToLeftParagraph )
558           {
559             if( direction1 )
560             {
561               *( modelVisualToLogicalCursorMap + index ) = logicalPosition1 + 1u;
562             }
563             else
564             {
565               *( modelVisualToLogicalCursorMap + index ) = logicalPosition0;
566             }
567           }
568           else
569           {
570             if( direction0 )
571             {
572               *( modelVisualToLogicalCursorMap + index ) = logicalPosition1;
573             }
574             else
575             {
576               *( modelVisualToLogicalCursorMap + index ) = logicalPosition0 + 1u;
577             }
578           }
579         }
580       }
581
582       if( bidirectionalParagraph &&
583           ( bidirectionalParagraph->characterRun.characterIndex + bidirectionalParagraph->characterRun.numberOfCharacters == index ) )
584       {
585         isRightToLeftParagraph = false;
586         ++bidirectionalParagraphIndex;
587         if( bidirectionalParagraphIndex < numberOfBidirectionalParagraphs )
588         {
589           bidirectionalParagraph = bidirectionalParagraphInfoBuffer + bidirectionalParagraphIndex;
590         }
591         else
592         {
593           bidirectionalParagraph = NULL;
594         }
595       }
596     }
597   }
598 }
599
600 void LogicalModel::ReplaceVisualToLogicalMap( CharacterIndex characterIndex,
601                                               Length numberOfCharactersToRemove,
602                                               const BidirectionalLineInfoRun* const bidirectionalInfo,
603                                               Length numberOfCharactersToInsert )
604 {
605 }
606
607 CharacterIndex LogicalModel::GetVisualCharacterIndex( CharacterIndex logicalCharacterIndex ) const
608 {
609   if( 0u == mLogicalToVisualMap.Count() )
610   {
611     // If there is no logical to visual info is because the whole text is left to right.
612     // Return the identity.
613     return logicalCharacterIndex;
614   }
615
616   return *( mLogicalToVisualMap.Begin() + logicalCharacterIndex );
617 }
618
619 CharacterIndex LogicalModel::GetLogicalCharacterIndex( CharacterIndex visualCharacterIndex ) const
620 {
621   if( 0u == mVisualToLogicalMap.Count() )
622   {
623     // If there is no visual to logical info is because the whole text is left to right.
624     // Return the identity.
625     return visualCharacterIndex;
626   }
627
628   return *( mVisualToLogicalMap.Begin() + visualCharacterIndex );
629 }
630
631 void LogicalModel::GetLogicalToVisualMap( CharacterIndex* logicalToVisualMap,
632                                           CharacterIndex characterIndex,
633                                           Length numberOfCharacters ) const
634 {
635   memcpy( logicalToVisualMap, mLogicalToVisualMap.Begin() + characterIndex, numberOfCharacters * sizeof( CharacterIndex ) );
636 }
637
638 void LogicalModel::GetVisualToLogicalMap( CharacterIndex* visualToLogicalMap,
639                                           CharacterIndex characterIndex,
640                                           Length numberOfCharacters ) const
641 {
642   memcpy( visualToLogicalMap, mVisualToLogicalMap.Begin() + characterIndex, numberOfCharacters * sizeof( CharacterIndex ) );
643 }
644
645 LogicalModel::~LogicalModel()
646 {
647 }
648
649 LogicalModel::LogicalModel()
650 {
651 }
652
653 } // namespace Text
654
655 } // namespace Toolkit
656
657 } // namespace Dali