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