Merge "Show CopyPaste Poup on long press" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
index 5df31dc..9e29628 100644 (file)
@@ -136,7 +136,8 @@ EventData::EventData( DecoratorPtr decorator )
   mUpdateLeftSelectionPosition( false ),
   mUpdateRightSelectionPosition( false ),
   mScrollAfterUpdatePosition( false ),
-  mScrollAfterDelete( false )
+  mScrollAfterDelete( false ),
+  mAllTextSelected( false )
 {}
 
 EventData::~EventData()
@@ -170,6 +171,11 @@ bool Controller::Impl::ProcessInputEvents()
           OnTapEvent( *iter );
           break;
         }
+        case Event::LONG_PRESS_EVENT:
+        {
+          OnLongPressEvent( *iter );
+          break;
+        }
         case Event::PAN_EVENT:
         {
           OnPanEvent( *iter );
@@ -237,6 +243,7 @@ bool Controller::Impl::ProcessInputEvents()
         leftScroll = true;
       }
 
+      SetPopupButtons();
       mEventData->mDecoratorUpdated = true;
       mEventData->mUpdateLeftSelectionPosition = false;
     }
@@ -253,6 +260,7 @@ bool Controller::Impl::ProcessInputEvents()
         rightScroll = true;
       }
 
+      SetPopupButtons();
       mEventData->mDecoratorUpdated = true;
       mEventData->mUpdateRightSelectionPosition = false;
     }
@@ -536,6 +544,15 @@ void Controller::Impl::OnPanEvent( const Event& event )
   }
 }
 
+void Controller::Impl::OnLongPressEvent( const Event& event )
+{
+  if  ( EventData::EDITING == mEventData->mState )
+  {
+    ChangeState ( EventData::EDITING_WITH_POPUP );
+    mEventData->mDecoratorUpdated = true;
+  }
+}
+
 void Controller::Impl::OnHandleEvent( const Event& event )
 {
   if( NULL == mEventData )
@@ -966,6 +983,50 @@ void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY
   RepositionSelectionHandles( selectionStart, selectionEnd );
 }
 
+void Controller::Impl::SetPopupButtons()
+{
+  /**
+   *  Sets the Popup buttons to be shown depending on State.
+   *
+   *  If SELECTING :  CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
+   *
+   *  If EDITING_WITH_POPUP : SELECT & SELECT_ALL
+   */
+
+  TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
+
+  if ( ( EventData::SELECTING == mEventData->mState ) || ( EventData::SELECTION_CHANGED == mEventData->mState ) )
+  {
+    buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
+
+    if ( !IsClipboardEmpty() )
+    {
+      buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
+      buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
+    }
+
+    if ( !mEventData->mAllTextSelected )
+    {
+      buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) );
+    }
+  }
+  else if  ( EventData::EDITING_WITH_POPUP == mEventData->mState )
+  {
+    if ( mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
+    {
+      buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
+    }
+
+    if ( !IsClipboardEmpty() )
+    {
+      buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
+      buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
+    }
+  }
+
+  mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
+}
+
 void Controller::Impl::ChangeState( EventData::State newState )
 {
   if( NULL == mEventData )
@@ -998,14 +1059,7 @@ void Controller::Impl::ChangeState( EventData::State newState )
       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
       if( mEventData->mGrabHandlePopupEnabled )
       {
-        TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
-        if ( !IsClipboardEmpty() )
-        {
-          buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
-          buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
-        }
-
-        mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
+        SetPopupButtons();
         mEventData->mDecorator->SetPopupActive( true );
       }
       mEventData->mDecoratorUpdated = true;
@@ -1014,14 +1068,8 @@ void Controller::Impl::ChangeState( EventData::State newState )
     {
       if( mEventData->mGrabHandlePopupEnabled )
       {
-        TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
-        if (  !IsClipboardEmpty() )
-        {
-          buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
-          buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
-        }
+        SetPopupButtons();
         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
-        mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
         mEventData->mDecorator->SetPopupActive( true );
       }
       mEventData->mDecoratorUpdated = true;
@@ -1062,15 +1110,7 @@ void Controller::Impl::ChangeState( EventData::State newState )
       }
       if( mEventData->mGrabHandlePopupEnabled )
       {
-        TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
-
-        if ( !IsClipboardEmpty() )
-        {
-          buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
-          buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
-        }
-
-        mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
+        SetPopupButtons();
         mEventData->mDecorator->SetPopupActive( true );
       }
       HideClipboard();
@@ -1205,6 +1245,7 @@ CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
 
   // Get the glyphs per character table.
   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
+  const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
 
   // If the vector is void, there is no right to left characters.
   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
@@ -1223,9 +1264,11 @@ CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
     // The character in logical order.
     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
 
+    // Get the script of the character.
+    const Script script = mLogicalModel->GetScript( characterLogicalOrderIndex );
+
     // The first glyph for that character in logical order.
     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
-
     // The number of glyphs for that character
     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
 
@@ -1239,12 +1282,25 @@ CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
 
     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
 
-    // Find the mid-point of the area containing the glyph
-    const float glyphCenter = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
+    // Prevents to jump the whole Latin ligatures like fi, ff, ...
+    const Length numberOfCharactersInLigature = ( TextAbstraction::LATIN == script ) ? *( charactersPerGlyphBuffer + glyphLogicalOrderIndex ) : 1u;
+    const float glyphAdvance = glyphMetrics.advance / static_cast<float>( numberOfCharactersInLigature );
 
-    if( visualX < glyphCenter )
+    for( GlyphIndex index = 0u; !matched && ( index < numberOfCharactersInLigature ); ++index )
+    {
+      // Find the mid-point of the area containing the glyph
+      const float glyphCenter = -glyphMetrics.xBearing + position.x + ( static_cast<float>( index ) + 0.5f ) * glyphAdvance;
+
+      if( visualX < glyphCenter )
+      {
+        visualIndex += index;
+        matched = true;
+        break;
+      }
+    }
+
+    if( matched )
     {
-      matched = true;
       break;
     }
   }
@@ -1258,6 +1314,7 @@ CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
 
   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
+
   return logicalIndex;
 }
 
@@ -1640,6 +1697,11 @@ void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
                                        cursorPosition.x,
                                        cursorPosition.y,
                                        cursorInfo.lineHeight );
+
+  // If selection handle at start of the text and other at end of the text then all text is selected.
+  const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
+  const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition );
+  mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() );
 }
 
 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
@@ -1734,7 +1796,6 @@ void Controller::Impl::ScrollTextToMatchCursor()
 
   if( cursorInfo.isSecondaryCursor )
   {
-    mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
                                          cursorInfo.secondaryPosition.x + offset.x,
                                          cursorInfo.secondaryPosition.y + offset.y,
@@ -1742,9 +1803,24 @@ void Controller::Impl::ScrollTextToMatchCursor()
                                          cursorInfo.lineHeight );
     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
   }
+
+  // Set which cursors are active according the state.
+  if( ( EventData::EDITING == mEventData->mState ) ||
+      ( EventData::EDITING_WITH_POPUP == mEventData->mState ) ||
+      ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) )
+  {
+    if( cursorInfo.isSecondaryCursor )
+    {
+      mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
+    }
+    else
+    {
+      mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
+    }
+  }
   else
   {
-    mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
+    mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
   }
 }