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