Fixes cursor status when editing with popup or grab handle.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller.cpp
index 889ca68..6f61789 100644 (file)
 
 // EXTERNAL INCLUDES
 #include <limits>
-#include <iostream>
 #include <dali/public-api/adaptor-framework/key.h>
 #include <dali/integration-api/debug.h>
+#include <dali/devel-api/adaptor-framework/clipboard-event-notifier.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/bidirectional-support.h>
 #include <dali-toolkit/internal/text/character-set-conversion.h>
 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
-#include <dali-toolkit/internal/text/multi-language-support.h>
-#include <dali-toolkit/internal/text/script-run.h>
-#include <dali-toolkit/internal/text/segmentation.h>
-#include <dali-toolkit/internal/text/shaper.h>
 #include <dali-toolkit/internal/text/text-controller-impl.h>
-#include <dali-toolkit/internal/text/text-io.h>
-#include <dali-toolkit/internal/text/text-view.h>
 
 namespace
 {
@@ -47,6 +41,12 @@ const float MAX_FLOAT = std::numeric_limits<float>::max();
 
 const std::string EMPTY_STRING("");
 
+float ConvertToEven( float value )
+{
+  int intValue(static_cast<int>( value ));
+  return static_cast<float>(intValue % 2 == 0) ? intValue : (intValue + 1);
+}
+
 } // namespace
 
 namespace Dali
@@ -76,6 +76,20 @@ void Controller::SetText( const std::string& text )
   // Remove the previously set text
   ResetText();
 
+  CharacterIndex lastCursorIndex = 0u;
+
+  if( mImpl->mEventData )
+  {
+    // If popup shown then hide it by switching to Editing state
+    if( ( EventData::SELECTING == mImpl->mEventData->mState )          ||
+        ( EventData::SELECTION_CHANGED == mImpl->mEventData->mState )  ||
+        ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
+        ( EventData::EDITING_WITH_GRAB_HANDLE == mImpl->mEventData->mState ) )
+    {
+      mImpl->ChangeState( EventData::EDITING );
+    }
+  }
+
   if( !text.empty() )
   {
     //  Convert text into UTF-32
@@ -93,11 +107,8 @@ void Controller::SetText( const std::string& text )
     DALI_ASSERT_DEBUG( text.size() >= characterCount && "Invalid UTF32 conversion length" );
     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText %p UTF8 size %d, UTF32 size %d\n", this, text.size(), mImpl->mLogicalModel->mText.Count() );
 
-    // Reset the cursor position
-    if( mImpl->mEventData )
-    {
-      mImpl->mEventData->mPrimaryCursorPosition = characterCount;
-    }
+    // To reset the cursor position
+    lastCursorIndex = characterCount;
 
     // Update the rest of the model during size negotiation
     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
@@ -113,6 +124,12 @@ void Controller::SetText( const std::string& text )
     ShowPlaceholderText();
   }
 
+  // Resets the cursor position.
+  ResetCursorPosition( lastCursorIndex );
+
+  // Scrolls the text to make the cursor visible.
+  ResetScrollPosition();
+
   mImpl->RequestRelayout();
 
   if( mImpl->mEventData )
@@ -313,7 +330,7 @@ bool Controller::RemoveText( int cursorOffset, int numberOfChars )
   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfChars %d\n",
                  this, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition, cursorOffset, numberOfChars );
 
-  if( ! mImpl->IsShowingPlaceholderText() )
+  if( !mImpl->IsShowingPlaceholderText() )
   {
     // Delete at current cursor position
     Vector<Character>& currentText = mImpl->mLogicalModel->mText;
@@ -332,8 +349,7 @@ bool Controller::RemoveText( int cursorOffset, int numberOfChars )
       numberOfChars = currentText.Count() - cursorIndex;
     }
 
-    if( cursorIndex >= 0 &&
-        (cursorIndex + numberOfChars) <= currentText.Count() )
+    if( (cursorIndex + numberOfChars) <= currentText.Count() )
     {
       Vector<Character>::Iterator first = currentText.Begin() + cursorIndex;
       Vector<Character>::Iterator last  = first + numberOfChars;
@@ -529,6 +545,9 @@ Vector3 Controller::GetNaturalSize()
     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize cached %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
   }
 
+  naturalSize.x = ConvertToEven( naturalSize.x );
+  naturalSize.y = ConvertToEven( naturalSize.y );
+
   return naturalSize;
 }
 
@@ -539,7 +558,7 @@ float Controller::GetHeightForWidth( float width )
   ProcessModifyEvents();
 
   Size layoutSize;
-  if( width != mImpl->mControlSize.width )
+  if( width != mImpl->mVisualModel->mControlSize.width )
   {
     // Operations that can be done only once until the text changes.
     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
@@ -586,9 +605,9 @@ bool Controller::Relayout( const Size& size )
   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
   {
     bool glyphsRemoved( false );
-    if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
+    if( 0u != mImpl->mVisualModel->mGlyphPositions.Count() )
     {
-      mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
+      mImpl->mVisualModel->mGlyphPositions.Clear();
       glyphsRemoved = true;
     }
     // Not worth to relayout if width or height is equal to zero.
@@ -596,9 +615,9 @@ bool Controller::Relayout( const Size& size )
     return glyphsRemoved;
   }
 
-  if( size != mImpl->mControlSize )
+  if( size != mImpl->mVisualModel->mControlSize )
   {
-    DALI_LOG_INFO( gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", mImpl->mControlSize.width, mImpl->mControlSize.height );
+    DALI_LOG_INFO( gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", mImpl->mVisualModel->mControlSize.width, mImpl->mVisualModel->mControlSize.height );
 
     // Operations that need to be done if the size changes.
     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
@@ -607,7 +626,7 @@ bool Controller::Relayout( const Size& size )
                                                              UPDATE_ACTUAL_SIZE        |
                                                              REORDER );
 
-    mImpl->mControlSize = size;
+    mImpl->mVisualModel->mControlSize = size;
   }
 
   // Make sure the model is up-to-date before layouting
@@ -615,7 +634,7 @@ bool Controller::Relayout( const Size& size )
   mImpl->UpdateModel( mImpl->mOperationsPending );
 
   Size layoutSize;
-  bool updated = DoRelayout( mImpl->mControlSize,
+  bool updated = DoRelayout( mImpl->mVisualModel->mControlSize,
                              mImpl->mOperationsPending,
                              layoutSize );
 
@@ -672,12 +691,6 @@ void Controller::ResetText()
   mImpl->mLogicalModel->mText.Clear();
   ClearModelData();
 
-  // Reset the cursor position
-  if( mImpl->mEventData )
-  {
-    mImpl->mEventData->mPrimaryCursorPosition = 0;
-  }
-
   // We have cleared everything including the placeholder-text
   mImpl->PlaceholderCleared();
 
@@ -688,6 +701,33 @@ void Controller::ResetText()
   mImpl->mOperationsPending = ALL_OPERATIONS;
 }
 
+void Controller::ResetCursorPosition( CharacterIndex cursorIndex )
+{
+  // Reset the cursor position
+  if( NULL != mImpl->mEventData )
+  {
+    mImpl->mEventData->mPrimaryCursorPosition = cursorIndex;
+
+    // Update the cursor if it's in editing mode.
+    if( ( EventData::EDITING == mImpl->mEventData->mState )            ||
+        ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
+        ( EventData::EDITING_WITH_GRAB_HANDLE == mImpl->mEventData->mState ) )
+    {
+      mImpl->mEventData->mUpdateCursorPosition = true;
+    }
+  }
+}
+
+void Controller::ResetScrollPosition()
+{
+  if( NULL != mImpl->mEventData )
+  {
+    // Reset the scroll position.
+    mImpl->mEventData->mScrollPosition = Vector2::ZERO;
+    mImpl->mEventData->mScrollAfterUpdatePosition = true;
+  }
+}
+
 void Controller::TextReplacedEvent()
 {
   // Reset buffers.
@@ -724,8 +764,13 @@ void Controller::TextInsertedEvent()
                                                            REORDER );
 
   // Queue a cursor reposition event; this must wait until after DoRelayout()
-  mImpl->mEventData->mUpdateCursorPosition = true;
-  mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
+  if( ( EventData::EDITING == mImpl->mEventData->mState )            ||
+      ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
+      ( EventData::EDITING_WITH_GRAB_HANDLE == mImpl->mEventData->mState ) )
+  {
+    mImpl->mEventData->mUpdateCursorPosition = true;
+    mImpl->mEventData->mScrollAfterUpdatePosition = true;
+  }
 }
 
 void Controller::TextDeletedEvent()
@@ -747,8 +792,7 @@ void Controller::TextDeletedEvent()
                                                            REORDER );
 
   // Queue a cursor reposition event; this must wait until after DoRelayout()
-  mImpl->mEventData->mUpdateCursorPosition = true;
-  mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
+  mImpl->mEventData->mScrollAfterDelete = true;
 }
 
 bool Controller::DoRelayout( const Size& size,
@@ -767,7 +811,7 @@ bool Controller::DoRelayout( const Size& size,
     // after the first time the text has been laid out.
     // Fill the vectors again.
 
-    Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
+    const Length numberOfGlyphs = mImpl->mVisualModel->mGlyphs.Count();
 
     if( 0u == numberOfGlyphs )
     {
@@ -776,16 +820,17 @@ bool Controller::DoRelayout( const Size& size,
       return true;
     }
 
-    Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
-    Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
-    Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
-    Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
-    Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
-    Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
+    const Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
+    const Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
+    const Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
+    const Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
+    const Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
+    const Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
+    const Character* const textBuffer = mImpl->mLogicalModel->mText.Begin();
 
     // Set the layout parameters.
     LayoutParameters layoutParameters( size,
-                                       mImpl->mLogicalModel->mText.Begin(),
+                                       textBuffer,
                                        lineBreakInfo.Begin(),
                                        wordBreakInfo.Begin(),
                                        ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
@@ -810,6 +855,9 @@ bool Controller::DoRelayout( const Size& size,
     Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
     glyphPositions.Resize( numberOfGlyphs );
 
+    // Whether the last character is a new paragraph character.
+    layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph( *( textBuffer + ( mImpl->mLogicalModel->mText.Count() - 1u ) ) );
+
     // Update the visual model.
     viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
                                                    glyphPositions,
@@ -827,7 +875,7 @@ bool Controller::DoRelayout( const Size& size,
         if( 0u != bidirectionalInfo.Count() )
         {
           // Get the lines
-          const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
+          const Length numberOfLines = mImpl->mVisualModel->mLines.Count();
 
           // Reorder the lines.
           Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
@@ -868,17 +916,6 @@ bool Controller::DoRelayout( const Size& size,
         }
       } // REORDER
 
-      // TODO: I'm working on a patch that changes the LayoutEngine::Align() method.
-      //       The layoutParameters is not needed and this call can be moved outside the if().
-      //       Then there is no need to do the layout again to change the alignment.
-      if( ALIGN & operations )
-      {
-        mImpl->mLayoutEngine.Align( layoutParameters,
-                                    layoutSize,
-                                    lines,
-                                    glyphPositions );
-      }
-
       // Sets the actual size.
       if( UPDATE_ACTUAL_SIZE & operations )
       {
@@ -891,6 +928,17 @@ bool Controller::DoRelayout( const Size& size,
     layoutSize = mImpl->mVisualModel->GetActualSize();
   }
 
+  if( ALIGN & operations )
+  {
+    // The laid-out lines.
+    Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
+
+    mImpl->mLayoutEngine.Align( layoutSize,
+                                lines );
+
+    viewUpdated = true;
+  }
+
   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
   return viewUpdated;
 }
@@ -929,15 +977,7 @@ void Controller::SetHorizontalAlignment( LayoutEngine::HorizontalAlignment align
     mImpl->mLayoutEngine.SetHorizontalAlignment( alignment );
 
     // Set the flag to redo the alignment operation.
-    // TODO : Is not needed re-layout and reorder again but with the current implementation it is.
-    //        Im working on a different patch to fix an issue with the alignment. When that patch
-    //        is in, this issue can be fixed.
-    const OperationsMask layoutOperations =  static_cast<OperationsMask>( LAYOUT             |
-                                                                          UPDATE_ACTUAL_SIZE |
-                                                                          ALIGN              |
-                                                                          REORDER );
-
-    mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
+    mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
 
     mImpl->RequestRelayout();
   }
@@ -955,16 +995,7 @@ void Controller::SetVerticalAlignment( LayoutEngine::VerticalAlignment alignment
     // Set the alignment.
     mImpl->mLayoutEngine.SetVerticalAlignment( alignment );
 
-    // Set the flag to redo the alignment operation.
-    // TODO : Is not needed re-layout and reorder again but with the current implementation it is.
-    //        Im working on a different patch to fix an issue with the alignment. When that patch
-    //        is in, this issue can be fixed.
-    const OperationsMask layoutOperations =  static_cast<OperationsMask>( LAYOUT             |
-                                                                          UPDATE_ACTUAL_SIZE |
-                                                                          ALIGN              |
-                                                                          REORDER );
-
-    mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
+    mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
 
     mImpl->RequestRelayout();
   }
@@ -980,7 +1011,12 @@ void Controller::CalculateTextAlignment( const Size& size )
   // Get the direction of the first character.
   const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
 
-  const Size& actualSize = mImpl->mVisualModel->GetActualSize();
+  Size actualSize = mImpl->mVisualModel->GetActualSize();
+  if( fabsf( actualSize.height ) < Math::MACHINE_EPSILON_1000 )
+  {
+    // Get the line height of the default font.
+    actualSize.height = mImpl->GetDefaultFontLineHeight();
+  }
 
   // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
   LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
@@ -1063,6 +1099,7 @@ void Controller::KeyboardFocusGainEvent()
       ShowPlaceholderText();
     }
 
+    mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered.
     mImpl->RequestRelayout();
   }
 }
@@ -1073,16 +1110,18 @@ void Controller::KeyboardFocusLostEvent()
 
   if( mImpl->mEventData )
   {
-    mImpl->ChangeState( EventData::INACTIVE );
-
-    if( mImpl->IsShowingPlaceholderText() )
+    if ( EventData::INTERRUPTED != mImpl->mEventData->mState )
     {
-      // Revert to regular placeholder-text when not editing
-      ShowPlaceholderText();
-    }
+      mImpl->ChangeState( EventData::INACTIVE );
 
-    mImpl->RequestRelayout();
+      if( mImpl->IsShowingPlaceholderText() )
+      {
+        // Revert to regular placeholder-text when not editing
+        ShowPlaceholderText();
+      }
+    }
   }
+  mImpl->RequestRelayout();
 }
 
 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
@@ -1114,28 +1153,19 @@ bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
     }
     else if( Dali::DALI_KEY_BACKSPACE == keyCode )
     {
-      DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE\n", this );
-
-      // IMF manager is no longer handling key-events
-      mImpl->ClearPreEditFlag();
-
-      // Remove the character before the current cursor position
-      bool removed = RemoveText( -1, 1 );
-
-      if( removed )
-      {
-        if( 0u == mImpl->mLogicalModel->mText.Count() )
-        {
-          ShowPlaceholderText();
-          mImpl->mEventData->mUpdateCursorPosition = true;
-        }
-        else
-        {
-          mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
-        }
-
-        textChanged = true;
-      }
+      textChanged = BackspaceKeyEvent();
+    }
+    else if ( IsKey( keyEvent,  Dali::DALI_KEY_POWER ) )
+    {
+      mImpl->ChangeState( EventData::INTERRUPTED ); // State is not INACTIVE as expect to return to edit mode.
+      // Avoids calling the InsertText() method which can delete selected text
+    }
+    else if ( IsKey( keyEvent, Dali::DALI_KEY_MENU ) ||
+              IsKey( keyEvent, Dali::DALI_KEY_HOME ) )
+    {
+      mImpl->ChangeState( EventData::INACTIVE );
+      // Menu/Home key behaviour does not allow edit mode to resume like Power key
+      // Avoids calling the InsertText() method which can delete selected text
     }
     else
     {
@@ -1145,11 +1175,13 @@ bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
       mImpl->ClearPreEditFlag();
 
       InsertText( keyString, COMMIT );
-
       textChanged = true;
     }
 
-    mImpl->ChangeState( EventData::EDITING ); // todo Confirm this is the best place to change the state of
+    if ( mImpl->mEventData->mState != EventData::INTERRUPTED &&  mImpl->mEventData->mState != EventData::INACTIVE )
+    {
+      mImpl->ChangeState( EventData::EDITING );
+    }
 
     mImpl->RequestRelayout();
   }
@@ -1165,7 +1197,7 @@ bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
 
 void Controller::InsertText( const std::string& text, Controller::InsertType type )
 {
-  bool removedPreEdit( false );
+  bool removedPrevious( false );
   bool maxLengthReached( false );
 
   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
@@ -1173,17 +1205,31 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ
                  this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"),
                  mImpl->mEventData->mPrimaryCursorPosition, mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
 
+  // TODO: At the moment the underline runs are only for pre-edit.
+  mImpl->mVisualModel->mUnderlineRuns.Clear();
+
   Vector<Character> utf32Characters;
   Length characterCount( 0u );
 
-  if( ! text.empty() )
+  // Remove the previous IMF pre-edit (predicitive text)
+  if( mImpl->mEventData &&
+      mImpl->mEventData->mPreEditFlag &&
+      0 != mImpl->mEventData->mPreEditLength )
   {
-    // The placeholder text is no longer needed
-    if( mImpl->IsShowingPlaceholderText() )
-    {
-      ResetText();
-    }
+    CharacterIndex offset = mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition;
+    removedPrevious = RemoveText( -static_cast<int>(offset), mImpl->mEventData->mPreEditLength );
 
+    mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
+    mImpl->mEventData->mPreEditLength = 0;
+  }
+  else
+  {
+    // Remove the previous Selection
+    removedPrevious = RemoveSelectedText();
+  }
+
+  if( ! text.empty() )
+  {
     //  Convert text into UTF-32
     utf32Characters.Resize( text.size() );
 
@@ -1199,22 +1245,19 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ
     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
   }
 
-  if( 0u != utf32Characters.Count() )
+  if( 0u != utf32Characters.Count() ) // Check if Utf8ToUtf32 conversion succeeded
   {
-    // Handle the IMF (predicitive text) state changes
-    if( mImpl->mEventData )
+    // The placeholder text is no longer needed
+    if( mImpl->IsShowingPlaceholderText() )
     {
-      if( mImpl->mEventData->mPreEditFlag &&
-          0 != mImpl->mEventData->mPreEditLength )
-      {
-        // Remove previous pre-edit text
-        CharacterIndex offset = mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition;
-        removedPreEdit = RemoveText( -static_cast<int>(offset), mImpl->mEventData->mPreEditLength );
+      ResetText();
+    }
 
-        mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
-        mImpl->mEventData->mPreEditLength = 0;
-      }
+    mImpl->ChangeState( EventData::EDITING );
 
+    // Handle the IMF (predicitive text) state changes
+    if( mImpl->mEventData )
+    {
       if( COMMIT == type )
       {
         // IMF manager is no longer handling key-events
@@ -1222,7 +1265,7 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ
       }
       else // PRE_EDIT
       {
-        if( ! mImpl->mEventData->mPreEditFlag )
+        if( !mImpl->mEventData->mPreEditFlag )
         {
           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Entered PreEdit state" );
 
@@ -1233,11 +1276,27 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ
         mImpl->mEventData->mPreEditLength = utf32Characters.Count();
         mImpl->mEventData->mPreEditFlag = true;
 
+        // Add the underline for the pre-edit text.
+        const GlyphIndex* const charactersToGlyphBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
+        const Length* const glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
+
+        const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mImpl->mEventData->mPreEditStartPosition );
+        const CharacterIndex lastPreEditCharacter = mImpl->mEventData->mPreEditStartPosition + ( ( mImpl->mEventData->mPreEditLength > 0u ) ? mImpl->mEventData->mPreEditLength - 1u : 0u );
+        const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter );
+        const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u );
+
+        GlyphRun underlineRun;
+        underlineRun.glyphIndex = glyphStart;
+        underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart;
+
+        // TODO: At the moment the underline runs are only for pre-edit.
+        mImpl->mVisualModel->mUnderlineRuns.PushBack( underlineRun );
+
         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
       }
     }
 
-    const Length numberOfCharactersInModel = mImpl->mLogicalModel->GetNumberOfCharacters();
+    const Length numberOfCharactersInModel = mImpl->mLogicalModel->mText.Count();
 
     // Restrict new text to fit within Maximum characters setting
     Length maxSizeOfNewText = std::min ( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
@@ -1262,8 +1321,16 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ
     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
   }
 
-  if( removedPreEdit ||
-      0 != utf32Characters.Count() )
+  if( 0u == mImpl->mLogicalModel->mText.Count() &&
+      mImpl->IsPlaceholderAvailable() )
+  {
+    // Show place-holder if empty after removing the pre-edit text
+    ShowPlaceholderText();
+    mImpl->mEventData->mUpdateCursorPosition = true;
+    mImpl->ClearPreEditFlag();
+  }
+  else if( removedPrevious ||
+           0 != utf32Characters.Count() )
   {
     // Queue an inserted event
     mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
@@ -1280,48 +1347,62 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ
   }
 }
 
+bool Controller::RemoveSelectedText()
+{
+  bool textRemoved( false );
+
+  if ( EventData::SELECTING         == mImpl->mEventData->mState ||
+       EventData::SELECTION_CHANGED == mImpl->mEventData->mState )
+  {
+    std::string removedString;
+    mImpl->RetrieveSelection( removedString, true );
+
+    if( !removedString.empty() )
+    {
+      textRemoved = true;
+      mImpl->ChangeState( EventData::EDITING );
+    }
+  }
+
+  return textRemoved;
+}
+
 void Controller::TapEvent( unsigned int tapCount, float x, float y )
 {
   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
 
   if( NULL != mImpl->mEventData )
   {
+    const bool isShowingPlaceholderText = mImpl->IsShowingPlaceholderText();
     if( 1u == tapCount )
     {
-      bool tapDuringEditMode( EventData::EDITING == mImpl->mEventData->mState );
-
-      if( ! mImpl->IsShowingPlaceholderText() &&
-          EventData::EDITING == mImpl->mEventData->mState )
+      if( !isShowingPlaceholderText &&
+          ( EventData::EDITING == mImpl->mEventData->mState ) )
       {
-        // Grab handle is not shown until a tap is received whilst EDITING
-        if( tapDuringEditMode )
-        {
-          mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
-        }
-        mImpl->mEventData->mDecorator->SetPopupActive( false );
+        mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
+      }
+      else if( EventData::EDITING_WITH_GRAB_HANDLE != mImpl->mEventData->mState  )
+      {
+        // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
+        mImpl->ChangeState( EventData::EDITING );
       }
 
-      mImpl->ChangeState( EventData::EDITING );
+      Event event( Event::TAP_EVENT );
+      event.p1.mUint = tapCount;
+      event.p2.mFloat = x;
+      event.p3.mFloat = y;
+      mImpl->mEventData->mEventQueue.push_back( event );
+
+      mImpl->RequestRelayout();
     }
-    else if( mImpl->mEventData->mSelectionEnabled &&
+    else if( !isShowingPlaceholderText &&
+             mImpl->mEventData->mSelectionEnabled &&
              ( 2u == tapCount ) )
     {
-      mImpl->ChangeState( EventData::SELECTING );
+      SelectEvent( x, y, false );
     }
   }
 
-  // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
-  if( mImpl->mEventData )
-  {
-    Event event( Event::TAP_EVENT );
-    event.p1.mUint = tapCount;
-    event.p2.mFloat = x;
-    event.p3.mFloat = y;
-    mImpl->mEventData->mEventQueue.push_back( event );
-
-    mImpl->RequestRelayout();
-  }
-
   // Reset keyboard as tap event has occurred.
   mImpl->ResetImfManager();
 }
@@ -1342,9 +1423,69 @@ void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
   }
 }
 
-void Controller::HandleEvent( HandleType handleType, HandleState state, float x, float y )
+void Controller::LongPressEvent( Gesture::State state, float x, float y  )
 {
-  DALI_ASSERT_DEBUG( mImpl->mEventData && "Controller::HandleEvent. Unexpected HandleEvent" );
+  DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
+
+  if  ( mImpl->IsShowingPlaceholderText() || mImpl->mLogicalModel->mText.Count() == 0u )
+  {
+    if ( mImpl->mEventData )
+    {
+      Event event( Event::LONG_PRESS_EVENT );
+      event.p1.mInt = state;
+      mImpl->mEventData->mEventQueue.push_back( event );
+      mImpl->RequestRelayout();
+    }
+  }
+  else if( mImpl->mEventData )
+  {
+    SelectEvent( x, y, false );
+  }
+}
+
+void Controller::SelectEvent( float x, float y, bool selectAll )
+{
+  if( mImpl->mEventData )
+  {
+    if ( mImpl->mEventData->mState == EventData::SELECTING )
+    {
+      mImpl->ChangeState( EventData::SELECTION_CHANGED );
+    }
+    else
+    {
+      mImpl->ChangeState( EventData::SELECTING );
+    }
+
+    if( selectAll )
+    {
+      Event event( Event::SELECT_ALL );
+      mImpl->mEventData->mEventQueue.push_back( event );
+    }
+    else
+    {
+      Event event( Event::SELECT );
+      event.p2.mFloat = x;
+      event.p3.mFloat = y;
+      mImpl->mEventData->mEventQueue.push_back( event );
+    }
+
+    mImpl->RequestRelayout();
+  }
+}
+
+void Controller::GetTargetSize( Vector2& targetSize )
+{
+  targetSize = mImpl->mVisualModel->mControlSize;
+}
+
+void Controller::AddDecoration( Actor& actor, bool needsClipping )
+{
+  mImpl->mControlInterface.AddDecoration( actor, needsClipping );
+}
+
+void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
+{
+  DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected DecorationEvent" );
 
   if( mImpl->mEventData )
   {
@@ -1380,6 +1521,12 @@ void Controller::HandleEvent( HandleType handleType, HandleState state, float x,
         mImpl->mEventData->mEventQueue.push_back( event );
         break;
       }
+      case LEFT_SELECTION_HANDLE_MARKER:
+      case RIGHT_SELECTION_HANDLE_MARKER:
+      {
+        // Markers do not move the handles.
+        break;
+      }
       case HANDLE_TYPE_COUNT:
       {
         DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
@@ -1390,6 +1537,90 @@ void Controller::HandleEvent( HandleType handleType, HandleState state, float x,
   }
 }
 
+void Controller::PasteText( const std::string& stringToPaste )
+{
+  InsertText( stringToPaste, Text::Controller::COMMIT );
+  mImpl->ChangeState( EventData::EDITING );
+  mImpl->RequestRelayout();
+}
+
+void Controller::PasteClipboardItemEvent()
+{
+  ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
+  std::string stringToPaste( notifier.GetContent() );
+  PasteText( stringToPaste );
+}
+
+void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button )
+{
+  if( NULL == mImpl->mEventData )
+  {
+    return;
+  }
+
+  switch( button )
+  {
+    case Toolkit::TextSelectionPopup::CUT:
+    {
+      mImpl->SendSelectionToClipboard( true ); // Synchronous call to modify text
+      mImpl->mOperationsPending = ALL_OPERATIONS;
+      if( 0u != mImpl->mLogicalModel->mText.Count() ||
+          !mImpl->IsPlaceholderAvailable() )
+      {
+        mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
+      }
+      else
+      {
+        ShowPlaceholderText();
+        mImpl->mEventData->mUpdateCursorPosition = true;
+      }
+      mImpl->RequestRelayout();
+      mImpl->mControlInterface.TextChanged();
+      break;
+    }
+    case Toolkit::TextSelectionPopup::COPY:
+    {
+      mImpl->SendSelectionToClipboard( false ); // Text not modified
+      mImpl->RequestRelayout(); // Handles, Selection Highlight, Popup
+      break;
+    }
+    case Toolkit::TextSelectionPopup::PASTE:
+    {
+      std::string stringToPaste("");
+      mImpl->GetTextFromClipboard( 0, stringToPaste ); // Paste latest item from system clipboard
+      PasteText( stringToPaste );
+      break;
+    }
+    case Toolkit::TextSelectionPopup::SELECT:
+    {
+      const Vector2& currentCursorPosition = mImpl->mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
+
+      if( mImpl->mEventData->mSelectionEnabled  )
+      {
+        // Creates a SELECT event.
+        SelectEvent( currentCursorPosition.x, currentCursorPosition.y, false );
+      }
+      break;
+    }
+    case Toolkit::TextSelectionPopup::SELECT_ALL:
+    {
+      // Creates a SELECT_ALL event
+      SelectEvent( 0.f, 0.f, true );
+      break;
+    }
+    case Toolkit::TextSelectionPopup::CLIPBOARD:
+    {
+      mImpl->ShowClipboard();
+      break;
+    }
+    case Toolkit::TextSelectionPopup::NONE:
+    {
+      // Nothing to do.
+      break;
+    }
+  }
+}
+
 ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
 {
   bool update( false );
@@ -1455,12 +1686,48 @@ ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, cons
   return callbackData;
 }
 
-
 Controller::~Controller()
 {
   delete mImpl;
 }
 
+bool Controller::BackspaceKeyEvent()
+{
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE\n", this );
+
+  // IMF manager is no longer handling key-events
+  mImpl->ClearPreEditFlag();
+
+  bool removed( false );
+
+  if ( EventData::SELECTING         == mImpl->mEventData->mState ||
+       EventData::SELECTION_CHANGED == mImpl->mEventData->mState )
+  {
+    removed = RemoveSelectedText();
+  }
+  else if( mImpl->mEventData->mPrimaryCursorPosition > 0 )
+  {
+    // Remove the character before the current cursor position
+    removed = RemoveText( -1, 1 );
+  }
+
+  if( removed )
+  {
+    if( 0u != mImpl->mLogicalModel->mText.Count() ||
+        !mImpl->IsPlaceholderAvailable() )
+    {
+      mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
+    }
+    else
+    {
+      ShowPlaceholderText();
+      mImpl->mEventData->mUpdateCursorPosition = true;
+    }
+  }
+
+  return removed;
+}
+
 void Controller::ShowPlaceholderText()
 {
   if( mImpl->IsPlaceholderAvailable() )