2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/text/logical-model-impl.h>
22 #include <dali-toolkit/internal/text/input-style.h>
23 #include <dali-toolkit/internal/text/text-run-container.h>
34 void FreeFontFamilyNames( Vector<FontDescriptionRun>& fontDescriptionRuns )
36 for( Vector<FontDescriptionRun>::Iterator it = fontDescriptionRuns.Begin(),
37 endIt = fontDescriptionRuns.End();
41 delete (*it).familyName;
44 fontDescriptionRuns.Clear();
47 LogicalModelPtr LogicalModel::New()
49 return LogicalModelPtr( new LogicalModel() );
52 Script LogicalModel::GetScript( CharacterIndex characterIndex ) const
54 // If this operation is too slow, consider a binary search.
56 for( Length index = 0u, length = mScriptRuns.Count(); index < length; ++index )
58 const ScriptRun* const scriptRun = mScriptRuns.Begin() + index;
60 if( ( scriptRun->characterRun.characterIndex <= characterIndex ) &&
61 ( characterIndex < scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters ) )
63 return scriptRun->script;
67 return TextAbstraction::UNKNOWN;
70 CharacterDirection LogicalModel::GetCharacterDirection( CharacterIndex characterIndex ) const
72 if( characterIndex >= mCharacterDirections.Count() )
74 // The model has no right to left characters, so the vector of directions is void.
78 return *( mCharacterDirections.Begin() + characterIndex );
81 CharacterIndex LogicalModel::GetLogicalCursorIndex( CharacterIndex visualCursorIndex )
83 // The total number of characters.
84 const Length numberOfCharacters = mText.Count();
86 const bool fetch = FetchBidirectionalLineInfo( visualCursorIndex );
89 // The character is not inside a bidi line.
90 return visualCursorIndex;
93 // The character's directions buffer.
94 const CharacterDirection* const modelCharacterDirections = mCharacterDirections.Begin();
96 // The bidirectional line info.
97 const BidirectionalLineInfoRun* const bidirectionalLineInfo = mBidirectionalLineInfo.Begin() + mBidirectionalLineIndex;
99 // Whether the paragraph starts with a right to left character.
100 const bool isRightToLeftParagraph = bidirectionalLineInfo->direction;
102 CharacterIndex logicalCursorIndex = 0u;
104 if( 0u == visualCursorIndex )
106 if( isRightToLeftParagraph )
108 logicalCursorIndex = numberOfCharacters;
110 else // else logical position is zero.
112 logicalCursorIndex = 0u;
115 else if( numberOfCharacters == visualCursorIndex )
117 if( isRightToLeftParagraph )
119 logicalCursorIndex = 0u;
121 else // else logical position is the number of characters.
123 logicalCursorIndex = numberOfCharacters;
128 // Get the character indexed by index - 1 and index
129 // and calculate the logical position according the directions of
130 // both characters and the direction of the paragraph.
132 const CharacterIndex previousVisualCursorIndex = visualCursorIndex - 1u;
133 const CharacterIndex previousLogicalCursorIndex = *( bidirectionalLineInfo->visualToLogicalMap + previousVisualCursorIndex - bidirectionalLineInfo->characterRun.characterIndex ) + bidirectionalLineInfo->characterRun.characterIndex;
134 const CharacterIndex currentLogicalCursorIndex = *( bidirectionalLineInfo->visualToLogicalMap + visualCursorIndex - bidirectionalLineInfo->characterRun.characterIndex ) + bidirectionalLineInfo->characterRun.characterIndex;
136 const CharacterDirection previousCharacterDirection = *( modelCharacterDirections + previousLogicalCursorIndex );
137 const CharacterDirection currentCharacterDirection = *( modelCharacterDirections + currentLogicalCursorIndex );
139 if( previousCharacterDirection == currentCharacterDirection )
141 // Both glyphs have the same direction.
142 if( previousCharacterDirection )
144 logicalCursorIndex = previousLogicalCursorIndex;
148 logicalCursorIndex = currentLogicalCursorIndex;
153 if( isRightToLeftParagraph )
155 if( currentCharacterDirection )
157 logicalCursorIndex = currentLogicalCursorIndex + 1u;
161 logicalCursorIndex = previousLogicalCursorIndex;
166 if( previousCharacterDirection )
168 logicalCursorIndex = currentLogicalCursorIndex;
172 logicalCursorIndex = previousLogicalCursorIndex + 1u;
178 return logicalCursorIndex;
181 CharacterIndex LogicalModel::GetLogicalCharacterIndex( CharacterIndex visualCharacterIndex )
183 const bool fetch = FetchBidirectionalLineInfo( visualCharacterIndex );
186 // The character is not inside a bidi line.
187 return visualCharacterIndex;
190 // The bidirectional line info.
191 const BidirectionalLineInfoRun* const bidirectionalLineInfo = mBidirectionalLineInfo.Begin() + mBidirectionalLineIndex;
193 return *( bidirectionalLineInfo->visualToLogicalMap + visualCharacterIndex - bidirectionalLineInfo->characterRun.characterIndex ) + bidirectionalLineInfo->characterRun.characterIndex;
196 bool LogicalModel::FetchBidirectionalLineInfo( CharacterIndex characterIndex )
198 // The number of bidirectional lines.
199 const Length numberOfBidirectionalLines = mBidirectionalLineInfo.Count();
201 if( 0u == numberOfBidirectionalLines )
203 // If there is no bidirectional info.
207 // Find the bidi line where the character is laid-out.
209 const BidirectionalLineInfoRun* const bidirectionalLineInfoBuffer = mBidirectionalLineInfo.Begin();
211 // Check first if the character is in the previously fetched line.
213 BidirectionalLineRunIndex bidiLineIndex = 0u;
214 CharacterIndex lastCharacterOfRightToLeftRun = 0u;
215 if( mBidirectionalLineIndex < numberOfBidirectionalLines )
217 const BidirectionalLineInfoRun& bidiLineRun = *( bidirectionalLineInfoBuffer + mBidirectionalLineIndex );
219 // Whether the character index is just after the last one. i.e The cursor position after the last character.
220 const bool isLastIndex = characterIndex == mText.Count();
222 const CharacterIndex lastCharacterOfRunPlusOne = bidiLineRun.characterRun.characterIndex + bidiLineRun.characterRun.numberOfCharacters;
223 if( ( bidiLineRun.characterRun.characterIndex <= characterIndex ) &&
224 ( ( characterIndex < lastCharacterOfRunPlusOne ) || ( isLastIndex && ( characterIndex == lastCharacterOfRunPlusOne ) ) ) )
226 // The character is in the previously fetched bidi line.
231 // The character is not in the previously fetched line.
235 // The given index is one after the last character, so it's not in a bidi line.
236 // Check if it's just after the last bidi line.
237 const BidirectionalLineRunIndex lastBidiLineIndex = numberOfBidirectionalLines - 1u;
238 const BidirectionalLineInfoRun& bidiLineRun = *( bidirectionalLineInfoBuffer + lastBidiLineIndex );
240 if( characterIndex == bidiLineRun.characterRun.characterIndex + bidiLineRun.characterRun.numberOfCharacters )
242 // The character is in the last bidi line.
243 mBidirectionalLineIndex = lastBidiLineIndex;
248 // Set the bidi line index from where to start the fetch.
250 if( characterIndex < bidiLineRun.characterRun.characterIndex )
252 // Start the fetch from the beginning.
257 // Start the fetch from the next line.
258 bidiLineIndex = mBidirectionalLineIndex + 1u;
259 lastCharacterOfRightToLeftRun = lastCharacterOfRunPlusOne - 1u;
264 // The character has not been found in the previously fetched bidi line.
265 for( Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLineInfoBuffer + bidiLineIndex,
266 endIt = mBidirectionalLineInfo.End();
268 ++it, ++bidiLineIndex )
270 const BidirectionalLineInfoRun& bidiLineRun = *it;
272 if( ( lastCharacterOfRightToLeftRun < characterIndex ) && ( characterIndex < bidiLineRun.characterRun.characterIndex ) )
274 // The character is not inside a bidi line.
278 const CharacterIndex lastCharacterOfRunPlusOne = bidiLineRun.characterRun.characterIndex + bidiLineRun.characterRun.numberOfCharacters;
279 lastCharacterOfRightToLeftRun = lastCharacterOfRunPlusOne - 1u;
280 if( ( bidiLineRun.characterRun.characterIndex <= characterIndex ) &&
281 ( characterIndex < lastCharacterOfRunPlusOne ) )
283 // Bidi line found. Fetch the line.
284 mBidirectionalLineIndex = bidiLineIndex;
292 BidirectionalLineRunIndex LogicalModel::GetBidirectionalLineInfo() const
294 return mBidirectionalLineIndex;
297 void LogicalModel::UpdateTextStyleRuns( CharacterIndex index, int numberOfCharacters )
299 const Length totalNumberOfCharacters = mText.Count();
301 // Process the color runs.
302 Vector<ColorRun> removedColorRuns;
303 UpdateCharacterRuns<ColorRun>( index,
305 totalNumberOfCharacters,
309 // Process the font description runs.
310 Vector<FontDescriptionRun> removedFontDescriptionRuns;
311 UpdateCharacterRuns<FontDescriptionRun>( index,
313 totalNumberOfCharacters,
314 mFontDescriptionRuns,
315 removedFontDescriptionRuns );
317 // Free memory allocated for the font family name.
318 FreeFontFamilyNames( removedFontDescriptionRuns );
321 void LogicalModel::RetrieveStyle( CharacterIndex index, InputStyle& style )
323 unsigned int runIndex = 0u;
325 // Set the text color.
326 bool colorOverriden = false;
327 unsigned int colorIndex = 0u;
328 const ColorRun* const colorRunsBuffer = mColorRuns.Begin();
329 for( Vector<ColorRun>::ConstIterator it = colorRunsBuffer,
330 endIt = mColorRuns.End();
334 const ColorRun& colorRun = *it;
336 if( ( colorRun.characterRun.characterIndex <= index ) &&
337 ( index < colorRun.characterRun.characterIndex + colorRun.characterRun.numberOfCharacters ) )
339 colorIndex = runIndex;
340 colorOverriden = true;
344 // Set the text's color if it's overriden.
347 style.textColor = ( *( colorRunsBuffer + colorIndex ) ).color;
348 style.isDefaultColor = false;
351 // Reset the run index.
354 // Set the font's parameters.
355 bool nameOverriden = false;
356 bool weightOverriden = false;
357 bool widthOverriden = false;
358 bool slantOverriden = false;
359 bool sizeOverriden = false;
360 unsigned int nameIndex = 0u;
361 unsigned int weightIndex = 0u;
362 unsigned int widthIndex = 0u;
363 unsigned int slantIndex = 0u;
364 unsigned int sizeIndex = 0u;
365 const FontDescriptionRun* const fontDescriptionRunsBuffer = mFontDescriptionRuns.Begin();
366 for( Vector<FontDescriptionRun>::ConstIterator it = fontDescriptionRunsBuffer,
367 endIt = mFontDescriptionRuns.End();
371 const FontDescriptionRun& fontDescriptionRun = *it;
373 if( ( fontDescriptionRun.characterRun.characterIndex <= index ) &&
374 ( index < fontDescriptionRun.characterRun.characterIndex + fontDescriptionRun.characterRun.numberOfCharacters ) )
376 if( fontDescriptionRun.familyDefined )
378 nameIndex = runIndex;
379 nameOverriden = true;
382 if( fontDescriptionRun.weightDefined )
384 weightIndex = runIndex;
385 weightOverriden = true;
388 if( fontDescriptionRun.widthDefined )
390 widthIndex = runIndex;
391 widthOverriden = true;
394 if( fontDescriptionRun.slantDefined )
396 slantIndex = runIndex;
397 slantOverriden = true;
400 if( fontDescriptionRun.sizeDefined )
402 sizeIndex = runIndex;
403 sizeOverriden = true;
408 // Set the font's family name if it's overriden.
411 const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + nameIndex );
413 style.familyName = std::string( fontDescriptionRun.familyName, fontDescriptionRun.familyLength );
414 style.familyDefined = true;
417 // Set the font's weight if it's overriden.
418 if( weightOverriden )
420 const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + weightIndex );
422 style.weight = fontDescriptionRun.weight;
423 style.weightDefined = true;
426 // Set the font's width if it's overriden.
429 const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + widthIndex );
431 style.width = fontDescriptionRun.width;
432 style.widthDefined = true;
435 // Set the font's slant if it's overriden.
438 const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + slantIndex );
440 style.slant = fontDescriptionRun.slant;
441 style.slantDefined = true;
444 // Set the font's size if it's overriden.
447 const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + sizeIndex );
449 style.size = static_cast<float>( fontDescriptionRun.size ) / 64.f;
450 style.sizeDefined = true;
454 void LogicalModel::ClearFontDescriptionRuns()
456 FreeFontFamilyNames( mFontDescriptionRuns );
459 void LogicalModel::CreateParagraphInfo( CharacterIndex startIndex,
460 Length numberOfCharacters )
462 const Length totalNumberOfCharacters = mLineBreakInfo.Count();
464 // Count the number of LINE_MUST_BREAK to reserve some space for the vector of paragraph's info.
465 Vector<CharacterIndex> paragraphs;
466 paragraphs.Reserve( numberOfCharacters );
467 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = mLineBreakInfo.Begin();
468 const CharacterIndex lastCharacterIndexPlusOne = startIndex + numberOfCharacters;
469 for( Length index = startIndex; index < lastCharacterIndexPlusOne; ++index )
471 if( TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + index ) )
473 paragraphs.PushBack( index );
477 // Whether the current paragraphs are updated or set from scratch.
478 const bool updateCurrentParagraphs = numberOfCharacters < totalNumberOfCharacters;
480 // Reserve space for current paragraphs plus new ones.
481 const Length numberOfNewParagraphs = paragraphs.Count();
482 const Length totalNumberOfParagraphs = mParagraphInfo.Count() + numberOfNewParagraphs;
483 mParagraphInfo.Resize( totalNumberOfParagraphs );
485 ParagraphRun* paragraphInfoBuffer = NULL;
486 Vector<ParagraphRun> newParagraphs;
488 if( updateCurrentParagraphs )
490 newParagraphs.Resize( numberOfNewParagraphs );
491 paragraphInfoBuffer = newParagraphs.Begin();
495 paragraphInfoBuffer = mParagraphInfo.Begin();
498 // Find where to insert the new paragraphs.
499 ParagraphRunIndex paragraphIndex = 0u;
500 CharacterIndex firstIndex = startIndex;
502 if( updateCurrentParagraphs )
504 for( Vector<ParagraphRun>::ConstIterator it = mParagraphInfo.Begin(),
505 endIt = mParagraphInfo.Begin() + totalNumberOfParagraphs - numberOfNewParagraphs;
509 const ParagraphRun& paragraph( *it );
511 if( startIndex < paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
513 firstIndex = paragraph.characterRun.characterIndex;
521 // Create the paragraph info.
522 ParagraphRunIndex newParagraphIndex = 0u;
523 for( Vector<CharacterIndex>::ConstIterator it = paragraphs.Begin(),
524 endIt = paragraphs.End();
526 ++it, ++newParagraphIndex )
528 const CharacterIndex index = *it;
530 ParagraphRun& paragraph = *( paragraphInfoBuffer + newParagraphIndex );
531 paragraph.characterRun.characterIndex = firstIndex;
532 paragraph.characterRun.numberOfCharacters = 1u + index - firstIndex;
534 firstIndex += paragraph.characterRun.numberOfCharacters;
538 // Insert the new paragraphs.
539 if( updateCurrentParagraphs )
541 mParagraphInfo.Insert( mParagraphInfo.Begin() + paragraphIndex,
542 newParagraphs.Begin(),
543 newParagraphs.End() );
545 mParagraphInfo.Resize( totalNumberOfParagraphs );
547 // Update the next paragraph indices.
548 for( Vector<ParagraphRun>::Iterator it = mParagraphInfo.Begin() + paragraphIndex + newParagraphs.Count(),
549 endIt = mParagraphInfo.End();
553 ParagraphRun& paragraph( *it );
555 paragraph.characterRun.characterIndex += numberOfCharacters;
560 void LogicalModel::FindParagraphs( CharacterIndex index,
561 Length numberOfCharacters,
562 Vector<ParagraphRunIndex>& paragraphs )
564 // Reserve som space for the paragraph indices.
565 paragraphs.Reserve( mParagraphInfo.Count() );
567 // Traverse the paragraphs to find which ones contain the given characters.
568 ParagraphRunIndex paragraphIndex = 0u;
569 for( Vector<ParagraphRun>::ConstIterator it = mParagraphInfo.Begin(),
570 endIt = mParagraphInfo.End();
572 ++it, ++paragraphIndex )
574 const ParagraphRun& paragraph( *it );
576 if( ( paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters > index ) &&
577 ( paragraph.characterRun.characterIndex < index + numberOfCharacters ) )
579 paragraphs.PushBack( paragraphIndex );
584 LogicalModel::~LogicalModel()
586 ClearFontDescriptionRuns();
589 LogicalModel::LogicalModel()
590 : mBidirectionalLineIndex( 0u )
596 } // namespace Toolkit