System Clipboard shown and items can be pasted
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
index 49b8f3d..5df31dc 100644 (file)
@@ -20,6 +20,7 @@
 
 // EXTERNAL INCLUDES
 #include <dali/public-api/adaptor-framework/key.h>
 
 // EXTERNAL INCLUDES
 #include <dali/public-api/adaptor-framework/key.h>
+#include <dali/integration-api/debug.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/bidirectional-support.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/bidirectional-support.h>
 namespace
 {
 
 namespace
 {
 
+#if defined(DEBUG_ENABLED)
+  Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_CONTROLS");
+#endif
+
 /**
  * @brief Some characters can be shaped in more than one glyph.
  * This struct is used to retrieve metrics from these group of glyphs.
 /**
  * @brief Some characters can be shaped in more than one glyph.
  * This struct is used to retrieve metrics from these group of glyphs.
@@ -123,14 +128,15 @@ EventData::EventData( DecoratorPtr decorator )
   mDecoratorUpdated( false ),
   mCursorBlinkEnabled( true ),
   mGrabHandleEnabled( true ),
   mDecoratorUpdated( false ),
   mCursorBlinkEnabled( true ),
   mGrabHandleEnabled( true ),
-  mGrabHandlePopupEnabled( false ),
-  mSelectionEnabled( false ),
+  mGrabHandlePopupEnabled( true ),
+  mSelectionEnabled( true ),
   mHorizontalScrollingEnabled( true ),
   mVerticalScrollingEnabled( false ),
   mUpdateCursorPosition( false ),
   mUpdateLeftSelectionPosition( false ),
   mUpdateRightSelectionPosition( false ),
   mHorizontalScrollingEnabled( true ),
   mVerticalScrollingEnabled( false ),
   mUpdateCursorPosition( false ),
   mUpdateLeftSelectionPosition( false ),
   mUpdateRightSelectionPosition( false ),
-  mScrollAfterUpdateCursorPosition( false )
+  mScrollAfterUpdatePosition( false ),
+  mScrollAfterDelete( false )
 {}
 
 EventData::~EventData()
 {}
 
 EventData::~EventData()
@@ -138,14 +144,14 @@ EventData::~EventData()
 
 bool Controller::Impl::ProcessInputEvents()
 {
 
 bool Controller::Impl::ProcessInputEvents()
 {
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
   if( NULL == mEventData )
   {
     // Nothing to do if there is no text input.
   if( NULL == mEventData )
   {
     // Nothing to do if there is no text input.
+    DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
     return false;
   }
 
     return false;
   }
 
-  mEventData->mDecoratorUpdated = false;
-
   if( mEventData->mDecorator )
   {
     for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
   if( mEventData->mDecorator )
   {
     for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
@@ -154,38 +160,38 @@ bool Controller::Impl::ProcessInputEvents()
     {
       switch( iter->type )
       {
     {
       switch( iter->type )
       {
-      case Event::KEYBOARD_FOCUS_GAIN_EVENT:
-      {
-        OnKeyboardFocus( true );
-        break;
-      }
-      case Event::KEYBOARD_FOCUS_LOST_EVENT:
-      {
-        OnKeyboardFocus( false );
-        break;
-      }
-      case Event::CURSOR_KEY_EVENT:
-      {
-        OnCursorKeyEvent( *iter );
-        break;
-      }
-      case Event::TAP_EVENT:
-      {
-        OnTapEvent( *iter );
-        break;
-      }
-      case Event::PAN_EVENT:
-      {
-        OnPanEvent( *iter );
-        break;
-      }
-      case Event::GRAB_HANDLE_EVENT:
-      case Event::LEFT_SELECTION_HANDLE_EVENT:
-      case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
-      {
-        OnHandleEvent( *iter );
-        break;
-      }
+        case Event::CURSOR_KEY_EVENT:
+        {
+          OnCursorKeyEvent( *iter );
+          break;
+        }
+        case Event::TAP_EVENT:
+        {
+          OnTapEvent( *iter );
+          break;
+        }
+        case Event::PAN_EVENT:
+        {
+          OnPanEvent( *iter );
+          break;
+        }
+        case Event::GRAB_HANDLE_EVENT:
+        case Event::LEFT_SELECTION_HANDLE_EVENT:
+        case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
+        {
+          OnHandleEvent( *iter );
+          break;
+        }
+        case Event::SELECT:
+        {
+          OnSelectEvent( *iter );
+          break;
+        }
+        case Event::SELECT_ALL:
+        {
+          OnSelectAllEvent();
+          break;
+        }
       }
     }
   }
       }
     }
   }
@@ -197,45 +203,74 @@ bool Controller::Impl::ProcessInputEvents()
 
     UpdateCursorPosition();
 
 
     UpdateCursorPosition();
 
-    if( mEventData->mScrollAfterUpdateCursorPosition )
+    if( mEventData->mScrollAfterUpdatePosition )
     {
     {
-      ScrollToMakeCursorVisible();
-      mEventData->mScrollAfterUpdateCursorPosition = false;
+      const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
+
+      ScrollToMakePositionVisible( primaryCursorPosition );
+      mEventData->mScrollAfterUpdatePosition = false;
     }
 
     mEventData->mDecoratorUpdated = true;
     mEventData->mUpdateCursorPosition = false;
   }
     }
 
     mEventData->mDecoratorUpdated = true;
     mEventData->mUpdateCursorPosition = false;
   }
-  else if( mEventData->mUpdateLeftSelectionPosition )
+  else if( mEventData->mScrollAfterDelete )
   {
   {
-    UpdateSelectionHandle( LEFT_SELECTION_HANDLE );
-
-    if( mEventData->mScrollAfterUpdateCursorPosition )
-    {
-      ScrollToMakeCursorVisible();
-      mEventData->mScrollAfterUpdateCursorPosition = false;
-    }
-
+    ScrollTextToMatchCursor();
     mEventData->mDecoratorUpdated = true;
     mEventData->mDecoratorUpdated = true;
-    mEventData->mUpdateLeftSelectionPosition = false;
+    mEventData->mScrollAfterDelete = false;
   }
   }
-  else if( mEventData->mUpdateRightSelectionPosition )
+  else
   {
   {
-    UpdateSelectionHandle( RIGHT_SELECTION_HANDLE );
+    bool leftScroll = false;
+    bool rightScroll = false;
 
 
-    if( mEventData->mScrollAfterUpdateCursorPosition )
+    if( mEventData->mUpdateLeftSelectionPosition )
     {
     {
-      ScrollToMakeCursorVisible();
-      mEventData->mScrollAfterUpdateCursorPosition = false;
+      UpdateSelectionHandle( LEFT_SELECTION_HANDLE );
+
+      if( mEventData->mScrollAfterUpdatePosition )
+      {
+        const Vector2& leftHandlePosition = mEventData->mDecorator->GetPosition( LEFT_SELECTION_HANDLE );
+
+        ScrollToMakePositionVisible( leftHandlePosition );
+        leftScroll = true;
+      }
+
+      mEventData->mDecoratorUpdated = true;
+      mEventData->mUpdateLeftSelectionPosition = false;
     }
 
     }
 
-    mEventData->mDecoratorUpdated = true;
-    mEventData->mUpdateRightSelectionPosition = false;
+    if( mEventData->mUpdateRightSelectionPosition )
+    {
+      UpdateSelectionHandle( RIGHT_SELECTION_HANDLE );
+
+      if( mEventData->mScrollAfterUpdatePosition )
+      {
+        const Vector2& rightHandlePosition = mEventData->mDecorator->GetPosition( RIGHT_SELECTION_HANDLE );
+
+        ScrollToMakePositionVisible( rightHandlePosition );
+        rightScroll = true;
+      }
+
+      mEventData->mDecoratorUpdated = true;
+      mEventData->mUpdateRightSelectionPosition = false;
+    }
+
+    if( leftScroll || rightScroll )
+    {
+      mEventData->mScrollAfterUpdatePosition = false;
+    }
   }
 
   mEventData->mEventQueue.clear();
 
   }
 
   mEventData->mEventQueue.clear();
 
-  return mEventData->mDecoratorUpdated;
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
+
+  const bool decoratorUpdated = mEventData->mDecoratorUpdated;
+  mEventData->mDecoratorUpdated = false;
+
+  return decoratorUpdated;
 }
 
 void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
 }
 
 void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
@@ -245,7 +280,7 @@ void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
 
   Vector<Character>& utf32Characters = mLogicalModel->mText;
 
 
   Vector<Character>& utf32Characters = mLogicalModel->mText;
 
-  const Length numberOfCharacters = mLogicalModel->GetNumberOfCharacters();
+  const Length numberOfCharacters = utf32Characters.Count();
 
   Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
   if( GET_LINE_BREAKS & operations )
 
   Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
   if( GET_LINE_BREAKS & operations )
@@ -398,24 +433,6 @@ void Controller::Impl::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfC
   }
 }
 
   }
 }
 
-void Controller::Impl::OnKeyboardFocus( bool hasFocus )
-{
-  if( NULL == mEventData )
-  {
-    // Nothing to do if there is no text input.
-    return;
-  }
-
-  if( !hasFocus )
-  {
-    ChangeState( EventData::INACTIVE );
-  }
-  else
-  {
-    ChangeState( EventData::EDITING );
-  }
-}
-
 void Controller::Impl::OnCursorKeyEvent( const Event& event )
 {
   if( NULL == mEventData )
 void Controller::Impl::OnCursorKeyEvent( const Event& event )
 {
   if( NULL == mEventData )
@@ -435,7 +452,7 @@ void Controller::Impl::OnCursorKeyEvent( const Event& event )
   }
   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
   {
   }
   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
   {
-    if( mLogicalModel->GetNumberOfCharacters() > mEventData->mPrimaryCursorPosition )
+    if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
     {
       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
     }
     {
       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
     }
@@ -450,49 +467,33 @@ void Controller::Impl::OnCursorKeyEvent( const Event& event )
   }
 
   mEventData->mUpdateCursorPosition = true;
   }
 
   mEventData->mUpdateCursorPosition = true;
-  mEventData->mScrollAfterUpdateCursorPosition = true;
+  mEventData->mScrollAfterUpdatePosition = true;
 }
 
 void Controller::Impl::OnTapEvent( const Event& event )
 {
 }
 
 void Controller::Impl::OnTapEvent( const Event& event )
 {
-  if( NULL == mEventData )
+  if( NULL != mEventData )
   {
   {
-    // Nothing to do if there is no text input.
-    return;
-  }
-
-  const unsigned int tapCount = event.p1.mUint;
+    const unsigned int tapCount = event.p1.mUint;
 
 
-  if( 1u == tapCount )
-  {
-    // Grab handle is not shown until a tap is received whilst EDITING
-    if( EventData::EDITING == mEventData->mState &&
-        !IsShowingPlaceholderText() )
+    if( 1u == tapCount )
     {
     {
-      if( mEventData->mGrabHandleEnabled )
+      if( ! IsShowingPlaceholderText() )
       {
       {
-        mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
-      }
-      mEventData->mDecorator->SetPopupActive( false );
-    }
-
-    ChangeState( EventData::EDITING );
-
-    const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
-    const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
-
-    mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
-                                                                yPosition );
+        const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
+        const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
 
 
-    mEventData->mUpdateCursorPosition = true;
-    mEventData->mScrollAfterUpdateCursorPosition = true;
-  }
-  else if( mEventData->mSelectionEnabled &&
-           ( 2u == tapCount ) )
-  {
-    ChangeState( EventData::SELECTING );
+        mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
+                                                                    yPosition );
+      }
+      else
+      {
+        mEventData->mPrimaryCursorPosition = 0u;
+      }
 
 
-    RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
+      mEventData->mUpdateCursorPosition = true;
+      mEventData->mScrollAfterUpdatePosition = true;
+    }
   }
 }
 
   }
 }
 
@@ -544,6 +545,7 @@ void Controller::Impl::OnHandleEvent( const Event& event )
   }
 
   const unsigned int state = event.p1.mUint;
   }
 
   const unsigned int state = event.p1.mUint;
+  const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
 
   if( HANDLE_PRESSED == state )
   {
 
   if( HANDLE_PRESSED == state )
   {
@@ -555,7 +557,7 @@ void Controller::Impl::OnHandleEvent( const Event& event )
 
     if( Event::GRAB_HANDLE_EVENT == event.type )
     {
 
     if( Event::GRAB_HANDLE_EVENT == event.type )
     {
-      ChangeState ( EventData::EDITING );
+      ChangeState ( EventData::GRAB_HANDLE_PANNING );
 
       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
       {
 
       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
       {
@@ -565,92 +567,403 @@ void Controller::Impl::OnHandleEvent( const Event& event )
     }
     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
     {
     }
     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
     {
-      if( handleNewPosition != mEventData->mLeftSelectionPosition )
+      ChangeState ( EventData::SELECTION_HANDLE_PANNING );
+
+      if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
+          ( handleNewPosition != mEventData->mRightSelectionPosition ) )
       {
         mEventData->mLeftSelectionPosition = handleNewPosition;
       {
         mEventData->mLeftSelectionPosition = handleNewPosition;
+
+        RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
+                                    mEventData->mRightSelectionPosition );
+
         mEventData->mUpdateLeftSelectionPosition = true;
       }
     }
     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
     {
         mEventData->mUpdateLeftSelectionPosition = true;
       }
     }
     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
     {
-      if( handleNewPosition != mEventData->mRightSelectionPosition )
+      ChangeState ( EventData::SELECTION_HANDLE_PANNING );
+
+      if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
+          ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
       {
         mEventData->mRightSelectionPosition = handleNewPosition;
       {
         mEventData->mRightSelectionPosition = handleNewPosition;
+
+        RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
+                                    mEventData->mRightSelectionPosition );
+
         mEventData->mUpdateRightSelectionPosition = true;
       }
     }
         mEventData->mUpdateRightSelectionPosition = true;
       }
     }
-  }
+  } // end ( HANDLE_PRESSED == state )
   else if( ( HANDLE_RELEASED == state ) ||
   else if( ( HANDLE_RELEASED == state ) ||
-           ( HANDLE_STOP_SCROLLING == state ) )
+           handleStopScrolling )
   {
   {
-    if( mEventData->mGrabHandlePopupEnabled )
+    CharacterIndex handlePosition = 0u;
+    if( handleStopScrolling )
     {
     {
-      ChangeState( EventData::EDITING_WITH_POPUP );
+      // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
+      const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
+      const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
+
+      handlePosition = GetClosestCursorIndex( xPosition, yPosition );
     }
     }
+
     if( Event::GRAB_HANDLE_EVENT == event.type )
     {
       mEventData->mUpdateCursorPosition = true;
 
     if( Event::GRAB_HANDLE_EVENT == event.type )
     {
       mEventData->mUpdateCursorPosition = true;
 
-      if( HANDLE_STOP_SCROLLING == state )
+      ChangeState( EventData::EDITING_WITH_POPUP );
+
+      if( handleStopScrolling )
       {
       {
-        // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
-        const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
-        const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
+        mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
+        mEventData->mPrimaryCursorPosition = handlePosition;
+      }
+    }
+    else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
+    {
+      ChangeState( EventData::SELECTING );
+
+      if( handleStopScrolling )
+      {
+        mEventData->mUpdateLeftSelectionPosition = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition);
+        mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
 
 
-        mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition, yPosition );
+        if( mEventData->mUpdateLeftSelectionPosition )
+        {
+          mEventData->mLeftSelectionPosition = handlePosition;
 
 
-        mEventData->mScrollAfterUpdateCursorPosition = true;
+          RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
+                                      mEventData->mRightSelectionPosition );
+        }
+      }
+    }
+    else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
+    {
+      ChangeState( EventData::SELECTING );
+
+      if( handleStopScrolling )
+      {
+        mEventData->mUpdateRightSelectionPosition = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
+        mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
+
+        if( mEventData->mUpdateRightSelectionPosition )
+        {
+          mEventData->mRightSelectionPosition = handlePosition;
+          RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
+                                      mEventData->mRightSelectionPosition );
+        }
       }
     }
       }
     }
+
     mEventData->mDecoratorUpdated = true;
     mEventData->mDecoratorUpdated = true;
-  }
+  } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
   else if( HANDLE_SCROLLING == state )
   {
     const float xSpeed = event.p2.mFloat;
     const Vector2& actualSize = mVisualModel->GetActualSize();
   else if( HANDLE_SCROLLING == state )
   {
     const float xSpeed = event.p2.mFloat;
     const Vector2& actualSize = mVisualModel->GetActualSize();
+    const Vector2 currentScrollPosition = mEventData->mScrollPosition;
 
     mEventData->mScrollPosition.x += xSpeed;
 
     ClampHorizontalScroll( actualSize );
 
 
     mEventData->mScrollPosition.x += xSpeed;
 
     ClampHorizontalScroll( actualSize );
 
-   mEventData->mDecoratorUpdated = true;
+    if( Vector2::ZERO == ( currentScrollPosition - mEventData->mScrollPosition ) )
+    {
+      // Notify the decorator there is no more text to scroll.
+      // The decorator won't send more scroll events.
+      mEventData->mDecorator->NotifyEndOfScroll();
+    }
+    else
+    {
+      const bool scrollRightDirection = xSpeed > 0.f;
+      const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
+      const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
+
+      if( Event::GRAB_HANDLE_EVENT == event.type )
+      {
+        ChangeState( EventData::GRAB_HANDLE_PANNING );
+
+        Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
+
+        // Position the grag handle close to either the left or right edge.
+        position.x = scrollRightDirection ? 0.f : mControlSize.width;
+
+        // Get the new handle position.
+        // The grab handle's position is in decorator coords. Need to transforms to text coords.
+        const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
+                                                                     position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
+
+        mEventData->mUpdateCursorPosition = mEventData->mPrimaryCursorPosition != handlePosition;
+        mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateCursorPosition;
+        mEventData->mPrimaryCursorPosition = handlePosition;
+      }
+      else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
+      {
+        // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
+        //       Think if something can be done to save power.
+
+        ChangeState( EventData::SELECTION_HANDLE_PANNING );
+
+        Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
+
+        // Position the selection handle close to either the left or right edge.
+        position.x = scrollRightDirection ? 0.f : mControlSize.width;
+
+        // Get the new handle position.
+        // The selection handle's position is in decorator coords. Need to transforms to text coords.
+        const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
+                                                                     position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
+
+        if( leftSelectionHandleEvent )
+        {
+          mEventData->mUpdateLeftSelectionPosition = handlePosition != mEventData->mLeftSelectionPosition;
+          mEventData->mLeftSelectionPosition = handlePosition;
+        }
+        else
+        {
+          mEventData->mUpdateRightSelectionPosition = handlePosition != mEventData->mRightSelectionPosition;
+          mEventData->mRightSelectionPosition = handlePosition;
+        }
+
+        if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
+        {
+          RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
+                                      mEventData->mRightSelectionPosition );
+
+          mEventData->mScrollAfterUpdatePosition = true;
+        }
+      }
+      mEventData->mDecoratorUpdated = true;
+    }
+  } // end ( HANDLE_SCROLLING == state )
+}
+
+void Controller::Impl::OnSelectEvent( const Event& event )
+{
+  if( NULL == mEventData )
+  {
+    // Nothing to do if there is no text.
+    return;
+  }
+
+  if( mEventData->mSelectionEnabled )
+  {
+    // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
+    const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
+    const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
+
+    const CharacterIndex leftPosition = mEventData->mLeftSelectionPosition;
+    const CharacterIndex rightPosition = mEventData->mRightSelectionPosition;
+
+    RepositionSelectionHandles( xPosition,
+                                yPosition );
+
+    mEventData->mUpdateLeftSelectionPosition = leftPosition != mEventData->mLeftSelectionPosition;
+    mEventData->mUpdateRightSelectionPosition = rightPosition != mEventData->mRightSelectionPosition;
+
+    mEventData->mScrollAfterUpdatePosition = ( ( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition ) &&
+                                               ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition ) );
   }
 }
 
   }
 }
 
-void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
+void Controller::Impl::OnSelectAllEvent()
 {
   if( NULL == mEventData )
   {
 {
   if( NULL == mEventData )
   {
-    // Nothing to do if there is no text input.
+    // Nothing to do if there is no text.
     return;
   }
 
     return;
   }
 
-  // TODO - Find which word was selected
+  if( mEventData->mSelectionEnabled )
+  {
+    RepositionSelectionHandles( 0u,
+                                mLogicalModel->mText.Count() );
 
 
-  const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
-  const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
+    mEventData->mScrollAfterUpdatePosition = true;
+    mEventData->mUpdateLeftSelectionPosition = true;
+    mEventData->mUpdateRightSelectionPosition = true;
+  }
+}
 
 
-  const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
-  const Vector<Vector2>::SizeType positionCount = positions.Count();
+void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetreival )
+{
+  if( mEventData->mLeftSelectionPosition ==  mEventData->mRightSelectionPosition )
+  {
+    // Nothing to select if handles are in the same place.
+    selectedText="";
+    return;
+  }
+
+  //Get start and end position of selection
+  uint32_t startOfSelectedText = mEventData->mLeftSelectionPosition;
+  uint32_t lengthOfSelectedText =  mEventData->mRightSelectionPosition - startOfSelectedText;
+
+  // Validate the start and end selection points
+  if( ( startOfSelectedText >= 0 ) && (  ( startOfSelectedText + lengthOfSelectedText ) <=  mLogicalModel->mText.Count() ) )
+  {
+    //Get text as a UTF8 string
+    Vector<Character>& utf32Characters = mLogicalModel->mText;
+
+    Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
+
+    if ( deleteAfterRetreival  ) // Only delete text if copied successfully
+    {
+      // Delete text between handles
+      Vector<Character>& currentText = mLogicalModel->mText;
+
+      Vector<Character>::Iterator first = currentText.Begin() + startOfSelectedText;
+      Vector<Character>::Iterator last  = first + lengthOfSelectedText;
+      currentText.Erase( first, last );
+    }
+    mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition;
+    mEventData->mScrollAfterDelete = true;
+    mEventData->mDecoratorUpdated = true;
+  }
+}
+
+void Controller::Impl::ShowClipboard()
+{
+  if ( mClipboard )
+  {
+    mClipboard.ShowClipboard();
+  }
+}
+
+void Controller::Impl::HideClipboard()
+{
+  if ( mClipboard )
+  {
+    mClipboard.HideClipboard();
+  }
+}
+
+bool Controller::Impl::CopyStringToClipboard( std::string& source )
+{
+  //Send string to clipboard
+  return ( mClipboard && mClipboard.SetItem( source ) );
+}
+
+void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
+{
+  std::string selectedText;
+  RetrieveSelection( selectedText, deleteAfterSending );
+  CopyStringToClipboard( selectedText );
+  ChangeState( EventData::EDITING );
+}
+
+void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retreivedString )
+{
+  if ( mClipboard )
+  {
+    retreivedString =  mClipboard.GetItem( itemIndex );
+  }
+}
+
+void Controller::Impl::RepositionSelectionHandles( CharacterIndex selectionStart, CharacterIndex selectionEnd )
+{
+  if( selectionStart == selectionEnd )
+  {
+    // Nothing to select if handles are in the same place.
+    return;
+  }
+
+  mEventData->mDecorator->ClearHighlights();
+
+  mEventData->mLeftSelectionPosition = selectionStart;
+  mEventData->mRightSelectionPosition = selectionEnd;
+
+  const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
+  const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
+  const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
+  const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
+
+  // TODO: Better algorithm to create the highlight box.
+  // TODO: Multi-line.
+
+  const Vector<LineRun>& lines = mVisualModel->mLines;
+  const LineRun& firstLine = *lines.Begin();
+  const float height = firstLine.ascender + -firstLine.descender;
+
+  const bool indicesSwapped = ( selectionStart > selectionEnd );
+  if( indicesSwapped )
+  {
+    std::swap( selectionStart, selectionEnd );
+  }
+
+  GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
+  GlyphIndex glyphEnd = *( charactersToGlyphBuffer + ( selectionEnd - 1u ) ) + *( glyphsPerCharacterBuffer + ( selectionEnd - 1u ) ) - 1u;
+
+  mEventData->mDecorator->SwapSelectionHandlesEnabled( firstLine.direction != indicesSwapped );
+
+  const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
+
+  for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
+  {
+    const GlyphInfo& glyph = *( glyphsBuffer + index );
+    const Vector2& position = *( positionsBuffer + index );
+
+    const float xPosition = position.x - glyph.xBearing + offset.x;
+    mEventData->mDecorator->AddHighlight( xPosition, offset.y, xPosition + glyph.advance, height );
+  }
+
+  CursorInfo primaryCursorInfo;
+  GetCursorPosition( mEventData->mLeftSelectionPosition,
+                     primaryCursorInfo );
+
+  CursorInfo secondaryCursorInfo;
+  GetCursorPosition( mEventData->mRightSelectionPosition,
+                     secondaryCursorInfo );
 
 
-  // Guard against glyphs which did not fit inside the layout
-  const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
+  const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
+  const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
 
 
-  if( count )
+  mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryPosition.x, primaryPosition.y, primaryCursorInfo.lineHeight );
+
+  mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, secondaryCursorInfo.lineHeight );
+
+  // Set the flag to update the decorator.
+  mEventData->mDecoratorUpdated = true;
+}
+
+void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
+{
+  if( NULL == mEventData )
   {
   {
-    float primaryX   = positions[0].x + mEventData->mScrollPosition.x;
-    float secondaryX = positions[count-1].x + glyphs[count-1].width + mEventData->mScrollPosition.x;
+    // Nothing to do if there is no text input.
+    return;
+  }
 
 
-    // TODO - multi-line selection
-    const Vector<LineRun>& lines = mVisualModel->mLines;
-    float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
+  if( IsShowingPlaceholderText() )
+  {
+    // Nothing to do if there is the place-holder text.
+    return;
+  }
 
 
-    mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE,     primaryX, mEventData->mScrollPosition.y, height );
-    mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryX, mEventData->mScrollPosition.y, height );
+  const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
+  const Length numberOfLines  = mVisualModel->mLines.Count();
+  if( 0 == numberOfGlyphs ||
+      0 == numberOfLines )
+  {
+    // Nothing to do if there is no text.
+    return;
+  }
+
+  // Find which word was selected
+  CharacterIndex selectionStart( 0 );
+  CharacterIndex selectionEnd( 0 );
+  FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
 
 
-    mEventData->mDecorator->ClearHighlights();
-    mEventData->mDecorator->AddHighlight( primaryX, mEventData->mScrollPosition.y, secondaryX, height + mEventData->mScrollPosition.y );
+  if( selectionStart == selectionEnd )
+  {
+    ChangeState( EventData::EDITING );
+    // Nothing to select. i.e. a white space, out of bounds
+    return;
   }
   }
+
+  RepositionSelectionHandles( selectionStart, selectionEnd );
 }
 
 void Controller::Impl::ChangeState( EventData::State newState )
 }
 
 void Controller::Impl::ChangeState( EventData::State newState )
@@ -674,6 +987,7 @@ void Controller::Impl::ChangeState( EventData::State newState )
       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
       mEventData->mDecorator->SetPopupActive( false );
       mEventData->mDecoratorUpdated = true;
       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
       mEventData->mDecorator->SetPopupActive( false );
       mEventData->mDecoratorUpdated = true;
+      HideClipboard();
     }
     else if ( EventData::SELECTING == mEventData->mState )
     {
     }
     else if ( EventData::SELECTING == mEventData->mState )
     {
@@ -682,6 +996,34 @@ void Controller::Impl::ChangeState( EventData::State newState )
       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
       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 );
+        mEventData->mDecorator->SetPopupActive( true );
+      }
+      mEventData->mDecoratorUpdated = true;
+    }
+    else if ( EventData::SELECTION_CHANGED  == mEventData->mState )
+    {
+      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->SetActiveCursor( ACTIVE_CURSOR_NONE );
+        mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
+        mEventData->mDecorator->SetPopupActive( true );
+      }
       mEventData->mDecoratorUpdated = true;
     }
     else if( EventData::EDITING == mEventData->mState )
       mEventData->mDecoratorUpdated = true;
     }
     else if( EventData::EDITING == mEventData->mState )
@@ -695,7 +1037,12 @@ void Controller::Impl::ChangeState( EventData::State newState )
       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
+      if( mEventData->mGrabHandlePopupEnabled )
+      {
+        mEventData->mDecorator->SetPopupActive( false );
+      }
       mEventData->mDecoratorUpdated = true;
       mEventData->mDecoratorUpdated = true;
+      HideClipboard();
     }
     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
     {
     }
     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
     {
@@ -704,16 +1051,58 @@ void Controller::Impl::ChangeState( EventData::State newState )
       {
         mEventData->mDecorator->StartCursorBlink();
       }
       {
         mEventData->mDecorator->StartCursorBlink();
       }
-      mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
       if( mEventData->mSelectionEnabled )
       {
       if( mEventData->mSelectionEnabled )
       {
-        mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
-        mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
+        mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
+        mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
+      }
+      else
+      {
+        mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
       }
       if( mEventData->mGrabHandlePopupEnabled )
       {
       }
       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 );
         mEventData->mDecorator->SetPopupActive( true );
       }
         mEventData->mDecorator->SetPopupActive( true );
       }
+      HideClipboard();
+      mEventData->mDecoratorUpdated = true;
+    }
+    else if ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
+    {
+      mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
+      mEventData->mDecorator->StopCursorBlink();
+      mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
+      mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
+      mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
+      if( mEventData->mGrabHandlePopupEnabled )
+      {
+        mEventData->mDecorator->SetPopupActive( false );
+      }
+      mEventData->mDecoratorUpdated = true;
+    }
+    else if ( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
+    {
+      mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
+      if( mEventData->mCursorBlinkEnabled )
+      {
+        mEventData->mDecorator->StartCursorBlink();
+      }
+      mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
+      mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
+      mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
+      if( mEventData->mGrabHandlePopupEnabled )
+      {
+        mEventData->mDecorator->SetPopupActive( false );
+      }
       mEventData->mDecoratorUpdated = true;
     }
   }
       mEventData->mDecoratorUpdated = true;
     }
   }
@@ -737,9 +1126,49 @@ LineIndex Controller::Impl::GetClosestLine( float y ) const
     }
   }
 
     }
   }
 
+  if( lineIndex == 0 )
+  {
+    return 0;
+  }
+
   return lineIndex-1;
 }
 
   return lineIndex-1;
 }
 
+void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
+{
+  CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
+  if( hitCharacter >= mLogicalModel->mText.Count() )
+  {
+    // Selection out of bounds.
+    return;
+  }
+
+  startIndex = hitCharacter;
+  endIndex = hitCharacter;
+
+  if( !TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] ) )
+  {
+    // Find the start and end of the text
+    for( startIndex = hitCharacter; startIndex > 0; --startIndex )
+    {
+      Character charCode = mLogicalModel->mText[ startIndex-1 ];
+      if( TextAbstraction::IsWhiteSpace( charCode ) )
+      {
+        break;
+      }
+    }
+    const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
+    for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
+    {
+      Character charCode = mLogicalModel->mText[ endIndex ];
+      if( TextAbstraction::IsWhiteSpace( charCode ) )
+      {
+        break;
+      }
+    }
+  }
+}
+
 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
                                                         float visualY )
 {
 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
                                                         float visualY )
 {
@@ -810,9 +1239,10 @@ CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
 
     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
 
 
     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
 
-    const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
+    // Find the mid-point of the area containing the glyph
+    const float glyphCenter = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
 
 
-    if( visualX < glyphX )
+    if( visualX < glyphCenter )
     {
       matched = true;
       break;
     {
       matched = true;
       break;
@@ -826,7 +1256,9 @@ CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
     visualIndex = endCharacter;
   }
 
     visualIndex = endCharacter;
   }
 
-  return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
+  logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
+  return logicalIndex;
 }
 
 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
 }
 
 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
@@ -836,7 +1268,7 @@ void Controller::Impl::GetCursorPosition( CharacterIndex logical,
 
   // Check if the logical position is the first or the last one of the text.
   const bool isFirstPosition = 0u == logical;
 
   // Check if the logical position is the first or the last one of the text.
   const bool isFirstPosition = 0u == logical;
-  const bool isLastPosition = mLogicalModel->GetNumberOfCharacters() == logical;
+  const bool isLastPosition = mLogicalModel->mText.Count() == logical;
 
   if( isFirstPosition && isLastPosition )
   {
 
   if( isFirstPosition && isLastPosition )
   {
@@ -859,7 +1291,7 @@ void Controller::Impl::GetCursorPosition( CharacterIndex logical,
     cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
 
     cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
 
-    cursorInfo.primaryPosition.x = 0.f;
+    cursorInfo.primaryPosition.x = 1.f;
     cursorInfo.primaryPosition.y = 0.f;
 
     // Nothing else to do.
     cursorInfo.primaryPosition.y = 0.f;
 
     // Nothing else to do.
@@ -1063,45 +1495,126 @@ CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index )
 
 void Controller::Impl::UpdateCursorPosition()
 {
 
 void Controller::Impl::UpdateCursorPosition()
 {
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
   if( NULL == mEventData )
   {
     // Nothing to do if there is no text input.
   if( NULL == mEventData )
   {
     // Nothing to do if there is no text input.
+    DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
     return;
   }
 
     return;
   }
 
-  CursorInfo cursorInfo;
-  GetCursorPosition( mEventData->mPrimaryCursorPosition,
-                     cursorInfo );
+  if( IsShowingPlaceholderText() )
+  {
+    // Do not want to use the place-holder text to set the cursor position.
 
 
-  const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
-  const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
+    // Use the line's height of the font's family set to set the cursor's size.
+    // If there is no font's family set, use the default font.
+    // Use the current alignment to place the cursor at the beginning, center or end of the box.
 
 
-  // Sets the cursor position.
-  mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
-                                       cursorPosition.x,
-                                       cursorPosition.y,
-                                       cursorInfo.primaryCursorHeight,
-                                       cursorInfo.lineHeight );
+    float lineHeight = 0.f;
 
 
-  // Sets the grab handle position.
-  mEventData->mDecorator->SetPosition( GRAB_HANDLE,
-                                       cursorPosition.x,
-                                       cursorPosition.y,
-                                       cursorInfo.lineHeight );
+    FontId defaultFontId = 0u;
+    if( NULL == mFontDefaults )
+    {
+      defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
+                                             EMPTY_STRING );
+    }
+    else
+    {
+      defaultFontId = mFontDefaults->GetFontId( mFontClient );
+    }
 
 
-  if( cursorInfo.isSecondaryCursor )
-  {
-    mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
-    mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
-                                         cursorInfo.secondaryPosition.x + offset.x,
-                                         cursorInfo.secondaryPosition.y + offset.y,
-                                         cursorInfo.secondaryCursorHeight,
-                                         cursorInfo.lineHeight );
+    Text::FontMetrics fontMetrics;
+    mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
+
+    lineHeight = fontMetrics.ascender - fontMetrics.descender;
+
+
+    Vector2 cursorPosition;
+
+    switch( mLayoutEngine.GetHorizontalAlignment() )
+    {
+      case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
+      {
+        cursorPosition.x = 1.f;
+        break;
+      }
+      case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
+      {
+        cursorPosition.x = floor( 0.5f * mControlSize.width );
+        break;
+      }
+      case LayoutEngine::HORIZONTAL_ALIGN_END:
+      {
+        cursorPosition.x = mControlSize.width;
+        break;
+      }
+    }
+
+    switch( mLayoutEngine.GetVerticalAlignment() )
+    {
+      case LayoutEngine::VERTICAL_ALIGN_TOP:
+      {
+        cursorPosition.y = 0.f;
+        break;
+      }
+      case LayoutEngine::VERTICAL_ALIGN_CENTER:
+      {
+        cursorPosition.y = floorf( 0.5f * ( mControlSize.height - lineHeight ) );
+        break;
+      }
+      case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
+      {
+        cursorPosition.y = mControlSize.height - lineHeight;
+        break;
+      }
+    }
+
+    mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
+                                         cursorPosition.x,
+                                         cursorPosition.y,
+                                         lineHeight,
+                                         lineHeight );
   }
   else
   {
   }
   else
   {
-    mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
+    CursorInfo cursorInfo;
+    GetCursorPosition( mEventData->mPrimaryCursorPosition,
+                       cursorInfo );
+
+    const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
+    const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
+
+    // Sets the cursor position.
+    mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
+                                         cursorPosition.x,
+                                         cursorPosition.y,
+                                         cursorInfo.primaryCursorHeight,
+                                         cursorInfo.lineHeight );
+    DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
+
+    // Sets the grab handle position.
+    mEventData->mDecorator->SetPosition( GRAB_HANDLE,
+                                         cursorPosition.x,
+                                         cursorPosition.y,
+                                         cursorInfo.lineHeight );
+
+    if( cursorInfo.isSecondaryCursor )
+    {
+      mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
+      mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
+                                           cursorInfo.secondaryPosition.x + offset.x,
+                                           cursorInfo.secondaryPosition.y + offset.y,
+                                           cursorInfo.secondaryCursorHeight,
+                                           cursorInfo.lineHeight );
+      DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
+    }
+    else
+    {
+      mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
+    }
   }
   }
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
 }
 
 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
 }
 
 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
@@ -1163,27 +1676,19 @@ void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
   }
 }
 
   }
 }
 
-void Controller::Impl::ScrollToMakeCursorVisible()
+void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
 {
 {
-  if( NULL == mEventData )
-  {
-    // Nothing to do if there is no text input.
-    return;
-  }
-
-  const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
-
   Vector2 offset;
   bool updateDecorator = false;
   Vector2 offset;
   bool updateDecorator = false;
-  if( primaryCursorPosition.x < 0.f )
+  if( position.x < 0.f )
   {
   {
-    offset.x = -primaryCursorPosition.x;
+    offset.x = -position.x;
     mEventData->mScrollPosition.x += offset.x;
     updateDecorator = true;
   }
     mEventData->mScrollPosition.x += offset.x;
     updateDecorator = true;
   }
-  else if( primaryCursorPosition.x > mControlSize.width )
+  else if( position.x > mControlSize.width )
   {
   {
-    offset.x = mControlSize.width - primaryCursorPosition.x;
+    offset.x = mControlSize.width - position.x;
     mEventData->mScrollPosition.x += offset.x;
     updateDecorator = true;
   }
     mEventData->mScrollPosition.x += offset.x;
     updateDecorator = true;
   }
@@ -1196,6 +1701,53 @@ void Controller::Impl::ScrollToMakeCursorVisible()
   // TODO : calculate the vertical scroll.
 }
 
   // TODO : calculate the vertical scroll.
 }
 
+void Controller::Impl::ScrollTextToMatchCursor()
+{
+  // Get the current cursor position in decorator coords.
+  const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
+
+  // Calculate the new cursor position.
+  CursorInfo cursorInfo;
+  GetCursorPosition( mEventData->mPrimaryCursorPosition,
+                     cursorInfo );
+
+  // Calculate the offset to match the cursor position before the character was deleted.
+  mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
+
+  ClampHorizontalScroll( mVisualModel->GetActualSize() );
+
+  const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
+  const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
+
+  // Sets the cursor position.
+  mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
+                                       cursorPosition.x,
+                                       cursorPosition.y,
+                                       cursorInfo.primaryCursorHeight,
+                                       cursorInfo.lineHeight );
+
+  // Sets the grab handle position.
+  mEventData->mDecorator->SetPosition( GRAB_HANDLE,
+                                       cursorPosition.x,
+                                       cursorPosition.y,
+                                       cursorInfo.lineHeight );
+
+  if( cursorInfo.isSecondaryCursor )
+  {
+    mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
+    mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
+                                         cursorInfo.secondaryPosition.x + offset.x,
+                                         cursorInfo.secondaryPosition.y + offset.y,
+                                         cursorInfo.secondaryCursorHeight,
+                                         cursorInfo.lineHeight );
+    DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
+  }
+  else
+  {
+    mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
+  }
+}
+
 void Controller::Impl::RequestRelayout()
 {
   mControlInterface.RequestTextRelayout();
 void Controller::Impl::RequestRelayout()
 {
   mControlInterface.RequestTextRelayout();