Dali-Text: Keyboard Shortcuts
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / bidirectional-support.cpp
old mode 100644 (file)
new mode 100755 (executable)
index fd85439..165ae0d
@@ -31,68 +31,37 @@ namespace Toolkit
 namespace Text
 {
 
-namespace
-{
-
-/**
- * @brief Get the lines of a paragraph.
- *
- * @param[in] paragraphInfo The paragraph.
- * @param[in] lines The lines.
- * @param[in] lineIndex Index pointing the first line to be checked.
- * @param[out] firstLine Index to the first line of the paragraph.
- * @param[out] numberOfLines The number of lines.
- */
-void GetLines( const BidirectionalParagraphInfoRun& paragraphInfo,
-               const Vector<LineRun>& lines,
-               unsigned int lineIndex,
-               unsigned int& firstLine,
-               unsigned int& numberOfLines )
+void SetBidirectionalInfo( const Vector<Character>& text,
+                           const Vector<ScriptRun>& scripts,
+                           const Vector<LineBreakInfo>& lineBreakInfo,
+                           CharacterIndex startIndex,
+                           Length numberOfCharacters,
+                           Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
+                           bool matchSystemLanguageDirection,
+                           Dali::LayoutDirection::Type layoutDirection )
 {
-  firstLine = lineIndex;
-  numberOfLines = 0u;
-
-  const CharacterIndex lastCharacterIndex = paragraphInfo.characterRun.characterIndex + paragraphInfo.characterRun.numberOfCharacters;
-  bool firstLineFound = false;
-
-  for( Vector<LineRun>::ConstIterator it = lines.Begin() + lineIndex,
-         endIt = lines.End();
+  // Find where to insert the new paragraphs.
+  BidirectionalRunIndex bidiInfoIndex = 0u;
+  for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
+         endIt = bidirectionalInfo.End();
        it != endIt;
        ++it )
   {
-    const LineRun& line = *it;
+    const BidirectionalParagraphInfoRun& run = *it;
 
-    if( ( line.characterRun.characterIndex + line.characterRun.numberOfCharacters > paragraphInfo.characterRun.characterIndex ) &&
-        ( lastCharacterIndex > line.characterRun.characterIndex ) )
+    if( startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters )
     {
-      firstLineFound = true;
-      ++numberOfLines;
-    }
-    else if( lastCharacterIndex <= line.characterRun.characterIndex )
-    {
-      // nothing else to do.
+      // Found where to insert the bidi info.
       break;
     }
-
-    if( !firstLineFound )
-    {
-      ++firstLine;
-    }
+    ++bidiInfoIndex;
   }
-}
 
-} // namespace
-
-void SetBidirectionalInfo( const Vector<Character>& text,
-                           const Vector<ScriptRun>& scripts,
-                           const Vector<LineBreakInfo>& lineBreakInfo,
-                           Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo )
-{
   // Traverse the script runs. If there is one with a right to left script, create the bidirectional info for the paragraph containing that script is needed.
   // From the bidirectional point of view, a paragraph is the piece of text between two LINE_MUST_BREAK.
 
   // Index pointing the first character of the current paragraph.
-  CharacterIndex paragraphCharacterIndex = 0u;
+  CharacterIndex paragraphCharacterIndex = startIndex;
 
   // Pointer to the text buffer.
   const Character* textBuffer = text.Begin();
@@ -100,139 +69,133 @@ void SetBidirectionalInfo( const Vector<Character>& text,
   // Pointer to the line break info buffer.
   const LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
 
-  // The number of characters.
-  const Length numberOfCharacters = text.Count();
-
   // Handle to the bidirectional info module in text-abstraction.
   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
 
+  const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
+
+  bool hasRightToLeftScript = false;
+
   for( Vector<ScriptRun>::ConstIterator it = scripts.Begin(),
          endIt = scripts.End();
        it != endIt;
        ++it )
   {
     const ScriptRun& scriptRun = *it;
-    const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters;
+    const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters - 1u;
+
+    if( startIndex > lastScriptRunIndex )
+    {
+      // Skip the run as it has already been processed.
+      continue;
+    }
+
+    if( lastCharacter <= scriptRun.characterRun.characterIndex )
+    {
+      // Do not get bidirectional info beyond startIndex + numberOfCharacters.
+      break;
+    }
+
+    if( !hasRightToLeftScript && scriptRun.isRightToLeft )
+    {
+      // The script is right to left.
+      hasRightToLeftScript = true;
+    }
 
-    if( TextAbstraction::IsRightToLeftScript( scriptRun.script ) && // The script is right to left.
-        ( lastScriptRunIndex > paragraphCharacterIndex ) )          // It isn't part of a previous paragraph.
+    if( TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + lastScriptRunIndex ) )
     {
-      // Find the paragraphs which contains this script run.
-      // Consider:
-      //  1) Different paragraphs may contain this script run.
-      //  ------||------------------- rtl sr ------------------------||-------------------
-      //  --||----- p -----||------------------ p -------------||-------- p ------||------
-      //
-      //  2) The paragraph which contains this script run may contain other right to left script runs.
-      //  -----||--- rtl sr ---||---- ltr sr ----||---------- rtl sr -----------||--------
-      //  -----||---------------------------------- p -----------------------------------|
-
-      while( lastScriptRunIndex > paragraphCharacterIndex )
+      // A new paragraph has been found.
+
+      if( hasRightToLeftScript )
       {
-        // There is a paragraph which contains the current script.
-
-        Length index = paragraphCharacterIndex;
-        while( ( index < numberOfCharacters ) && ( paragraphCharacterIndex < lastScriptRunIndex ) )
-        {
-          if( TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + index ) )
-          {
-            if( index >= scriptRun.characterRun.characterIndex )
-            {
-              // The Bidirectional run must have the same number of characters than the paragraph.
-              BidirectionalParagraphInfoRun bidirectionalRun;
-              bidirectionalRun.characterRun.characterIndex = paragraphCharacterIndex;
-              bidirectionalRun.characterRun.numberOfCharacters = ( index - paragraphCharacterIndex ) + 1u; // The must break character is part of the paragrah.
-
-              // Create the bidirectional info for the whole paragraph and store the index to the table with this info in the run.
-              bidirectionalRun.bidirectionalInfoIndex = bidirectionalSupport.CreateInfo( textBuffer + bidirectionalRun.characterRun.characterIndex,
-                                                                                         bidirectionalRun.characterRun.numberOfCharacters );
-
-              bidirectionalInfo.PushBack( bidirectionalRun );
-            }
-
-            // Update the character index of the next paragraph.
-            paragraphCharacterIndex = index + 1u;
-          }
-          ++index;
-        }
-
-        // The last character is always a must-break, so there is no need to check if there is characters left.
+        // The Bidirectional run must have the same number of characters than the paragraph.
+        BidirectionalParagraphInfoRun bidirectionalRun;
+        bidirectionalRun.characterRun.characterIndex = paragraphCharacterIndex;
+        bidirectionalRun.characterRun.numberOfCharacters = ( lastScriptRunIndex - paragraphCharacterIndex ) + 1u; // The must break character is part of the paragrah.
+
+        // Create the bidirectional info for the whole paragraph and store the index to the table with this info in the run.
+        bidirectionalRun.bidirectionalInfoIndex = bidirectionalSupport.CreateInfo( textBuffer + bidirectionalRun.characterRun.characterIndex,
+                                                                                   bidirectionalRun.characterRun.numberOfCharacters,
+                                                                                   matchSystemLanguageDirection,
+                                                                                   layoutDirection );
+
+        bidirectionalRun.direction = bidirectionalSupport.GetParagraphDirection( bidirectionalRun.bidirectionalInfoIndex );
+
+        bidirectionalInfo.Insert( bidirectionalInfo.Begin() + bidiInfoIndex, bidirectionalRun );
+        ++bidiInfoIndex;
       }
-    }
-  }
-}
 
-void ReorderLines( const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
-                   Vector<LineRun>& lineRuns,
-                   Vector<BidirectionalLineInfoRun>& lineInfoRuns )
-{
-  // Handle to the bidirectional info module in text-abstraction.
-  TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
+      // Point to the next paragraph.
+      paragraphCharacterIndex = lastScriptRunIndex + 1u;
 
-  // Keep an index to the first line to be checked if it's contained inside the paragraph.
-  // Avoids check the lines from the beginning for each paragraph.
-  unsigned int lineIndex = 0u;
+      // Reset whether there is a right to left script.
+      hasRightToLeftScript = false;
+    }
+  }
 
-  for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
+  // Update indices of the bidi runs.
+  for( Vector<BidirectionalParagraphInfoRun>::Iterator it = bidirectionalInfo.Begin() + bidiInfoIndex,
          endIt = bidirectionalInfo.End();
        it != endIt;
        ++it )
   {
-    const BidirectionalParagraphInfoRun& paragraphInfo = *it;
-    const CharacterDirection direction = bidirectionalSupport.GetParagraphDirection( paragraphInfo.bidirectionalInfoIndex );
-
-    // Get the lines for this paragraph.
-    unsigned int firstLine = 0u;
-    unsigned int numberOfLines = 0u;
-
-    // Get an index to the first line and the number of lines of the current paragraph.
-    GetLines( paragraphInfo,
-              lineRuns,
-              lineIndex,
-              firstLine,
-              numberOfLines );
-
-    lineIndex = firstLine + numberOfLines;
-
-    // Traverse the lines and reorder them
-    for( Vector<LineRun>::Iterator lineIt = lineRuns.Begin() + firstLine,
-           endLineIt = lineRuns.Begin() + firstLine + numberOfLines;
-           lineIt != endLineIt;
-         ++lineIt )
-    {
-      LineRun& line = *lineIt;
+    BidirectionalParagraphInfoRun& run = *it;
+
+    run.characterRun.characterIndex += numberOfCharacters;
+  }
+}
 
-      // Sets the paragraph's direction.
-      line.direction = direction;
+void ReorderLine( const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
+                  Vector<BidirectionalLineInfoRun>& lineInfoRuns,
+                  BidirectionalLineRunIndex bidiLineIndex,
+                  CharacterIndex startIndex,
+                  Length numberOfCharacters,
+                  CharacterDirection direction )
+{
+  // Handle to the bidirectional info module in text-abstraction.
+  TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
 
-      // Creates a bidirectional info for the line run.
-      BidirectionalLineInfoRun lineInfoRun;
-      lineInfoRun.characterRun.characterIndex = line.characterRun.characterIndex;
-      lineInfoRun.characterRun.numberOfCharacters = line.characterRun.numberOfCharacters;
-      lineInfoRun.direction = direction;
+  // Creates a bidirectional info for the line run.
+  BidirectionalLineInfoRun lineInfoRun;
+  lineInfoRun.characterRun.characterIndex = startIndex;
+  lineInfoRun.characterRun.numberOfCharacters = numberOfCharacters;
+  lineInfoRun.direction = direction;
+  lineInfoRun.isIdentity = true;
 
-      // Allocate space for the conversion maps.
-      // The memory is freed after the visual to logical to visual conversion tables are built in the logical model.
-      lineInfoRun.visualToLogicalMap = reinterpret_cast<CharacterIndex*>( malloc( line.characterRun.numberOfCharacters * sizeof( CharacterIndex ) ) );
+  // Allocate space for the conversion maps.
+  // The memory is freed after the visual to logical to visual conversion tables are built in the logical model.
+  lineInfoRun.visualToLogicalMap = reinterpret_cast<CharacterIndex*>( malloc( numberOfCharacters * sizeof( CharacterIndex ) ) );
 
-      if( NULL != lineInfoRun.visualToLogicalMap )
+  if( nullptr != lineInfoRun.visualToLogicalMap )
+  {
+    // Reorders the line.
+    bidirectionalSupport.Reorder( bidirectionalParagraphInfo.bidirectionalInfoIndex,
+                                  lineInfoRun.characterRun.characterIndex - bidirectionalParagraphInfo.characterRun.characterIndex,
+                                  lineInfoRun.characterRun.numberOfCharacters,
+                                  lineInfoRun.visualToLogicalMap );
+
+    // For those LTR lines inside a bidirectional paragraph.
+    // It will save to relayout the line after reordering.
+    for( unsigned int i=0; i<numberOfCharacters; ++i )
+    {
+      if( i != *( lineInfoRun.visualToLogicalMap + i ) )
       {
-        // Reorders the line.
-        bidirectionalSupport.Reorder( paragraphInfo.bidirectionalInfoIndex,
-                                      line.characterRun.characterIndex - paragraphInfo.characterRun.characterIndex,
-                                      line.characterRun.numberOfCharacters,
-                                      lineInfoRun.visualToLogicalMap );
+        lineInfoRun.isIdentity = false;
+        break;
       }
-
-      // Push the run into the vector.
-      lineInfoRuns.PushBack( lineInfoRun );
     }
   }
+
+  // Push the run into the vector.
+  lineInfoRuns.Insert( lineInfoRuns.Begin() + bidiLineIndex, lineInfoRun );
 }
 
 bool GetMirroredText( const Vector<Character>& text,
-                      Vector<Character>& mirroredText,
-                      const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo )
+                      const Vector<CharacterDirection>& directions,
+                      const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
+                      CharacterIndex startIndex,
+                      Length numberOfCharacters,
+                      Vector<Character>& mirroredText )
 {
   bool hasTextMirrored = false;
 
@@ -242,6 +205,10 @@ bool GetMirroredText( const Vector<Character>& text,
   mirroredText = text;
 
   Character* mirroredTextBuffer = mirroredText.Begin();
+  CharacterDirection* directionsBuffer = directions.Begin();
+
+  CharacterIndex index = startIndex;
+  const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
 
   // Traverse the paragraphs and mirror the right to left ones.
   for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
@@ -249,10 +216,24 @@ bool GetMirroredText( const Vector<Character>& text,
        it != endIt;
        ++it )
   {
-    const BidirectionalParagraphInfoRun& run = *it;
+    const BidirectionalParagraphInfoRun& paragraph = *it;
+
+    if( index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
+    {
+      // Skip the paragraph as it has already been processed.
+      continue;
+    }
+
+    if( lastCharacter <= paragraph.characterRun.characterIndex )
+    {
+      // Do not get mirror characters beyond startIndex + numberOfCharacters.
+      break;
+    }
 
-    const bool tmpMirrored = bidirectionalSupport.GetMirroredText( mirroredTextBuffer + run.characterRun.characterIndex,
-                                                                   run.characterRun.numberOfCharacters );
+    index += paragraph.characterRun.numberOfCharacters;
+    const bool tmpMirrored = bidirectionalSupport.GetMirroredText( mirroredTextBuffer + paragraph.characterRun.characterIndex,
+                                                                   directionsBuffer + paragraph.characterRun.characterIndex,
+                                                                   paragraph.characterRun.numberOfCharacters );
 
     hasTextMirrored = hasTextMirrored || tmpMirrored;
   }
@@ -261,13 +242,36 @@ bool GetMirroredText( const Vector<Character>& text,
 }
 
 void GetCharactersDirection( const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
+                             Length totalNumberOfCharacters,
+                             CharacterIndex startIndex,
+                             Length numberOfCharacters,
                              Vector<CharacterDirection>& directions )
 {
   // Handle to the bidirectional info module in text-abstraction.
   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
 
-  CharacterIndex index = 0u;
-  CharacterDirection* directionsBuffer = directions.Begin();
+  // Resize the vector.
+  directions.Resize( totalNumberOfCharacters );
+
+  // Whether the current buffer is being updated or is set from scratch.
+  const bool updateCurrentBuffer = numberOfCharacters < totalNumberOfCharacters;
+
+  CharacterDirection* directionsBuffer = NULL;
+  Vector<CharacterDirection> newDirections;
+
+  if( updateCurrentBuffer )
+  {
+    newDirections.Resize( numberOfCharacters );
+    directionsBuffer = newDirections.Begin();
+  }
+  else
+  {
+    directionsBuffer = directions.Begin();
+  }
+
+  const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
+  CharacterIndex index = startIndex;
+
   for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
          endIt = bidirectionalInfo.End();
        it != endIt;
@@ -275,17 +279,46 @@ void GetCharactersDirection( const Vector<BidirectionalParagraphInfoRun>& bidire
   {
     const BidirectionalParagraphInfoRun& paragraph = *it;
 
-    // Fills with left to right those paragraphs without right to left characters.
-    memset( directionsBuffer + index, false, ( paragraph.characterRun.characterIndex - index ) * sizeof( bool ) );
-    index += paragraph.characterRun.numberOfCharacters;
+    if( index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
+    {
+      // Skip the paragraph as it has already been processed.
+      continue;
+    }
+
+    if( lastCharacter <= paragraph.characterRun.characterIndex )
+    {
+      // Do not get the character directions beyond startIndex + numberOfCharacters.
+      break;
+    }
+
+    // Set the directions of any previous left to right characters.
+    const Length numberOfLeftToRightCharacters = paragraph.characterRun.characterIndex - index;
+    if( numberOfLeftToRightCharacters > 0u )
+    {
+      memset( directionsBuffer + index - startIndex, false, numberOfLeftToRightCharacters * sizeof( bool ) );
+    }
 
+    // Set the directions of the bidirectional text.
     bidirectionalSupport.GetCharactersDirection( paragraph.bidirectionalInfoIndex,
-                                                 directionsBuffer + paragraph.characterRun.characterIndex,
+                                                 directionsBuffer + paragraph.characterRun.characterIndex - startIndex,
                                                  paragraph.characterRun.numberOfCharacters );
+
+    // Update the index.
+    index += paragraph.characterRun.numberOfCharacters + numberOfLeftToRightCharacters;
   }
 
   // Fills with left to right those paragraphs without right to left characters.
-  memset( directionsBuffer + index, false, ( directions.Count() - index ) * sizeof( bool ) );
+  memset( directionsBuffer + index - startIndex, false, ( lastCharacter - index ) * sizeof( bool ) );
+
+  // If the direction info is updated, it needs to be inserted in the model.
+  if( updateCurrentBuffer )
+  {
+    // Insert the directions in the given buffer.
+    directions.Insert( directions.Begin() + startIndex,
+                       newDirections.Begin(),
+                       newDirections.End() );
+    directions.Resize( totalNumberOfCharacters );
+  }
 }
 
 } // namespace Text