TextController re-organization. 98/85698/2
authorVictor Cebollada <v.cebollada@samsung.com>
Fri, 26 Aug 2016 08:38:24 +0000 (09:38 +0100)
committerVictor Cebollada <v.cebollada@samsung.com>
Fri, 26 Aug 2016 13:46:04 +0000 (14:46 +0100)
* Some test cases added to increase the coverage.

Change-Id: I209f0821805825b8b8b81b2d8e11b9db7a551c29
Signed-off-by: Victor Cebollada <v.cebollada@samsung.com>
automated-tests/src/dali-toolkit-internal/CMakeLists.txt
automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Controller.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/dali-toolkit-test-suite-utils.h
dali-toolkit/internal/controls/text-controls/text-field-impl.cpp
dali-toolkit/internal/text/text-controller.cpp
dali-toolkit/internal/text/text-controller.h

index b23014b..bb55422 100644 (file)
@@ -17,6 +17,7 @@ SET(TC_SOURCES
  utc-Dali-Text-Shaping.cpp
  utc-Dali-VisualModel.cpp
  utc-Dali-Text-Layout.cpp
+ utc-Dali-Text-Controller.cpp
 )
 
 # Append list of test harness files (Won't get parsed for test cases)
diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Controller.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Controller.cpp
new file mode 100644 (file)
index 0000000..7480435
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <iostream>
+
+#include <stdlib.h>
+#include <limits>
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <dali-toolkit/internal/text/text-controller.h>
+
+using namespace Dali;
+using namespace Toolkit;
+using namespace Text;
+
+namespace
+{
+
+const char* const OPTION_SELECT_WORD("option-select_word"); // "Select Word" popup option.
+const char* const OPTION_SELECT_ALL("option-select_all");   // "Select All" popup option.
+const char* const OPTION_CUT("optionCut");                  // "Cut" popup option.
+const char* const OPTION_COPY("optionCopy");                // "Copy" popup option.
+const char* const OPTION_PASTE("optionPaste");              // "Paste" popup option.
+const char* const OPTION_CLIPBOARD("optionClipboard");      // "Clipboard" popup option.
+
+const Size CONTROL_SIZE( 300.f, 60.f );
+
+class ControlImpl : public ControlInterface
+{
+public:
+  ControlImpl()
+  : ControlInterface()
+  {}
+
+  virtual ~ControlImpl()
+  {}
+
+  virtual void AddDecoration( Actor& actor, bool needsClipping )
+  {}
+
+  virtual void RequestTextRelayout()
+  {}
+
+  virtual void TextChanged()
+  {}
+
+  virtual void MaxLengthReached()
+  {}
+
+  virtual void InputStyleChanged( InputStyle::Mask inputStyleMask )
+  {}
+};
+
+std::string gClipboardText;
+void ContentSelectedCallback( ClipboardEventNotifier& notifier )
+{
+  gClipboardText = notifier.GetContent();
+}
+
+} // namespace
+
+int UtcDaliTextController(void)
+{
+  tet_infoline(" UtcDaliTextController");
+  ToolkitTestApplication application;
+
+  // Creates a text controller.
+  ControlImpl controlImpl;
+  ControllerPtr controller = Controller::New( controlImpl );
+
+  DALI_TEST_CHECK( controller );
+
+  tet_result(TET_PASS);
+  END_TEST;
+}
+
+int UtcDaliTextControllerEnableCursorBlinking(void)
+{
+  tet_infoline(" UtcDaliTextControllerEnableCursorBlinking");
+  ToolkitTestApplication application;
+
+  // Creates a text controller.
+  ControlImpl controlImpl;
+  ControllerPtr controller = Controller::New( controlImpl );
+
+  DALI_TEST_CHECK( controller );
+
+  // There is no text input enabled.
+  DALI_TEST_CHECK( !controller->GetEnableCursorBlink() );
+
+  // Enable the text input.
+  // Creates a decorator.
+  Text::DecoratorPtr decorator = Text::Decorator::New( *controller,
+                                                       *controller );
+
+  // Enables the text input.
+  controller->EnableTextInput( decorator );
+
+  // Enables the cursor blink.
+  controller->SetEnableCursorBlink( true );
+
+  DALI_TEST_CHECK( controller->GetEnableCursorBlink() );
+
+  // Disables the cursor blink.
+  controller->SetEnableCursorBlink( false );
+
+  DALI_TEST_CHECK( !controller->GetEnableCursorBlink() );
+
+  tet_result(TET_PASS);
+  END_TEST;
+}
+
+int UtcDaliTextControllerImfEvent(void)
+{
+  tet_infoline(" UtcDaliTextController");
+  ToolkitTestApplication application;
+
+  // Creates a text controller.
+  ControlImpl controlImpl;
+  ControllerPtr controller = Controller::New( controlImpl );
+
+  std::string text;
+  ImfManager::ImfEventData imfEvent;
+
+  DALI_TEST_CHECK( controller );
+
+  // Enable the text input.
+  // Creates a decorator.
+  Text::DecoratorPtr decorator = Text::Decorator::New( *controller,
+                                                       *controller );
+
+  // Enables the text input.
+  controller->EnableTextInput( decorator );
+
+  // Creates an ImfManager.
+  ImfManager imfManager = ImfManager::Get();
+
+  // Send VOID event.
+  imfEvent = ImfManager::ImfEventData( ImfManager::VOID, "", 0, 0 );
+  controller->OnImfEvent( imfManager, imfEvent );
+
+  controller->GetText( text );
+  DALI_TEST_CHECK( text.empty() );
+
+  // Send COMMIT event.
+  imfEvent = ImfManager::ImfEventData( ImfManager::COMMIT, "Hello ", 0, 6 );
+  controller->OnImfEvent( imfManager, imfEvent );
+
+  // Force to update the model.
+  controller->GetNaturalSize();
+
+  controller->GetText( text );
+  DALI_TEST_EQUALS( "Hello ", text, TEST_LOCATION );
+
+  // Send PREEDIT event
+  imfEvent = ImfManager::ImfEventData( ImfManager::PREEDIT, "w", 6, 1 );
+  controller->OnImfEvent( imfManager, imfEvent );
+
+  // Force to update the model.
+  controller->GetNaturalSize();
+
+  controller->GetText( text );
+  DALI_TEST_EQUALS( "Hello w", text, TEST_LOCATION );
+
+  // Send DELETESURROUNDING event
+  imfEvent = ImfManager::ImfEventData( ImfManager::DELETESURROUNDING, "", -1, 1 );
+  controller->OnImfEvent( imfManager, imfEvent );
+
+  // Force to update the model.
+  controller->GetNaturalSize();
+
+  controller->GetText( text );
+  DALI_TEST_EQUALS( "Hello ", text, TEST_LOCATION );
+
+  // Send PREEDIT event
+  imfEvent = ImfManager::ImfEventData( ImfManager::PREEDIT, "wo", 6, 2 );
+  controller->OnImfEvent( imfManager, imfEvent );
+
+  // Force to update the model.
+  controller->GetNaturalSize();
+
+  controller->GetText( text );
+  DALI_TEST_EQUALS( "Hello wo", text, TEST_LOCATION );
+
+  // Send GETSURROUNDING event
+  imfEvent = ImfManager::ImfEventData( ImfManager::GETSURROUNDING, "", 0, 0 );
+  controller->OnImfEvent( imfManager, imfEvent );
+
+  controller->GetText( text );
+  DALI_TEST_EQUALS( "Hello wo", text, TEST_LOCATION );
+
+  tet_result(TET_PASS);
+  END_TEST;
+}
+
+int UtcDaliTextControllerTextPopupButtonTouched(void)
+{
+  tet_infoline(" UtcDaliTextControllerTextPopupButtonTouched");
+  ToolkitTestApplication application;
+
+  // Creates a text controller.
+  ControlImpl controlImpl;
+  ControllerPtr controller = Controller::New( controlImpl );
+
+  DALI_TEST_CHECK( controller );
+
+  std::string text;
+  PushButton button;
+  Property::Map attributes;
+
+  // Enable the text input.
+  // Creates a decorator.
+  Text::DecoratorPtr decorator = Text::Decorator::New( *controller,
+                                                       *controller );
+
+  // Enables the text input.
+  controller->EnableTextInput( decorator );
+
+  // Creates the text's popup.
+  TextSelectionPopupCallbackInterface& callbackInterface = *controller;
+  TextSelectionPopup textPopup = TextSelectionPopup::New( &callbackInterface );
+
+  Toolkit::TextSelectionPopup::Buttons buttonsToEnable = static_cast<Toolkit::TextSelectionPopup::Buttons>( TextSelectionPopup::CUT        |
+                                                                                                            TextSelectionPopup::COPY       |
+                                                                                                            TextSelectionPopup::PASTE      |
+                                                                                                            TextSelectionPopup::SELECT     |
+                                                                                                            TextSelectionPopup::SELECT_ALL |
+                                                                                                            TextSelectionPopup::CLIPBOARD );
+
+  textPopup.EnableButtons( buttonsToEnable );
+  Stage::GetCurrent().Add( textPopup );
+  textPopup.ShowPopup();
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Sets some text.
+  controller->SetText( "Hello world" );
+
+  // Select the whole text.
+  button = PushButton::DownCast( textPopup.FindChildByName( OPTION_SELECT_ALL ) );
+  DALI_TEST_CHECK( button );
+
+  button.DoAction( "buttonClick", attributes );
+
+  // Call relayout to process the input events.
+  controller->Relayout( CONTROL_SIZE );
+
+  // Cut the text.
+  button = PushButton::DownCast( textPopup.FindChildByName( OPTION_CUT ) );
+  DALI_TEST_CHECK( button );
+
+  button.DoAction( "buttonClick", attributes );
+
+  // Force to update the model.
+  controller->GetNaturalSize();
+
+  controller->GetText( text );
+  DALI_TEST_CHECK( text.empty() );
+
+  // Set text again.
+  controller->SetText( "Hello world" );
+
+  // Select the whole text.
+  button = PushButton::DownCast( textPopup.FindChildByName( OPTION_SELECT_ALL ) );
+  DALI_TEST_CHECK( button );
+
+  button.DoAction( "buttonClick", attributes );
+
+  // Call relayout to process the input events.
+  controller->Relayout( CONTROL_SIZE );
+
+  // Copy to the clipboard.
+  button = PushButton::DownCast( textPopup.FindChildByName( OPTION_COPY ) );
+  DALI_TEST_CHECK( button );
+
+  button.DoAction( "buttonClick", attributes );
+
+  // Call relayout to process the input events.
+  controller->Relayout( CONTROL_SIZE );
+
+  // Cut the text.
+  button = PushButton::DownCast( textPopup.FindChildByName( OPTION_CUT ) );
+  DALI_TEST_CHECK( button );
+
+  button.DoAction( "buttonClick", attributes );
+
+  // Force to update the model.
+  controller->GetNaturalSize();
+
+  controller->GetText( text );
+  DALI_TEST_CHECK( text.empty() );
+
+  ClipboardEventNotifier clipboardEventNotifier = ClipboardEventNotifier::Get();
+  clipboardEventNotifier.ContentSelectedSignal().Connect( &ContentSelectedCallback );
+
+  // Paste the text.
+  button = PushButton::DownCast( textPopup.FindChildByName( OPTION_PASTE ) );
+  DALI_TEST_CHECK( button );
+
+  button.DoAction( "buttonClick", attributes );
+
+  // Call relayout to process the input events.
+  controller->Relayout( CONTROL_SIZE );
+
+  DALI_TEST_EQUALS( "Hello world", gClipboardText, TEST_LOCATION );
+
+  // Show the clipboard.
+  button = PushButton::DownCast( textPopup.FindChildByName( OPTION_CLIPBOARD ) );
+  DALI_TEST_CHECK( button );
+
+  button.DoAction( "buttonClick", attributes );
+
+  tet_result(TET_PASS);
+  END_TEST;
+}
index e53798f..50a5828 100644 (file)
@@ -23,5 +23,7 @@
 #include <dali-test-suite-utils.h>
 #include "toolkit-test-application.h"
 #include "toolkit-application.h"
+#include "toolkit-imf-manager.h"
+#include "toolkit-clipboard-event-notifier.h"
 
 #endif // __DALI_TOOLKIT_TEST_SUITE_UTILS_H__
index 13b0323..4ca9a85 100644 (file)
@@ -213,7 +213,7 @@ void TextField::SetProperty( BaseObject* object, Property::Index index, const Pr
           const std::string text = value.Get< std::string >();
           DALI_LOG_INFO( gLogFilter, Debug::General, "TextField %p PLACEHOLDER_TEXT %s\n", impl.mController.Get(), text.c_str() );
 
-          impl.mController->SetPlaceholderText( PLACEHOLDER_TYPE_INACTIVE, text );
+          impl.mController->SetPlaceholderText( Controller::PLACEHOLDER_TYPE_INACTIVE, text );
         }
         break;
       }
@@ -224,7 +224,7 @@ void TextField::SetProperty( BaseObject* object, Property::Index index, const Pr
           const std::string text = value.Get< std::string >();
           DALI_LOG_INFO( gLogFilter, Debug::General, "TextField %p PLACEHOLDER_TEXT_FOCUSED %s\n", impl.mController.Get(), text.c_str() );
 
-          impl.mController->SetPlaceholderText( PLACEHOLDER_TYPE_ACTIVE, text );
+          impl.mController->SetPlaceholderText( Controller::PLACEHOLDER_TYPE_ACTIVE, text );
         }
         break;
       }
@@ -738,7 +738,7 @@ Property::Value TextField::GetProperty( BaseObject* object, Property::Index inde
         if( impl.mController )
         {
           std::string text;
-          impl.mController->GetPlaceholderText( PLACEHOLDER_TYPE_INACTIVE, text );
+          impl.mController->GetPlaceholderText( Controller::PLACEHOLDER_TYPE_INACTIVE, text );
           value = text;
         }
         break;
@@ -748,7 +748,7 @@ Property::Value TextField::GetProperty( BaseObject* object, Property::Index inde
         if( impl.mController )
         {
           std::string text;
-          impl.mController->GetPlaceholderText( PLACEHOLDER_TYPE_ACTIVE, text );
+          impl.mController->GetPlaceholderText( Controller::PLACEHOLDER_TYPE_ACTIVE, text );
           value = text;
         }
         break;
index e10556c..6dfb99e 100644 (file)
@@ -98,11 +98,15 @@ FontDescriptionRun& UpdateSelectionFontStyleRun( EventData* eventData,
   return fontDescriptionRun;
 }
 
+// public : Constructor.
+
 ControllerPtr Controller::New( ControlInterface& controlInterface )
 {
   return ControllerPtr( new Controller( controlInterface ) );
 }
 
+// public : Configure the text controller.
+
 void Controller::EnableTextInput( DecoratorPtr decorator )
 {
   if( NULL == mImpl->mEventData )
@@ -257,6 +261,107 @@ bool Controller::IsSmoothHandlePanEnabled() const
   return false;
 }
 
+void Controller::SetMaximumNumberOfCharacters( Length maxCharacters )
+{
+  mImpl->mMaximumNumberOfCharacters = maxCharacters;
+}
+
+int Controller::GetMaximumNumberOfCharacters()
+{
+  return mImpl->mMaximumNumberOfCharacters;
+}
+
+void Controller::SetEnableCursorBlink( bool enable )
+{
+  DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "TextInput disabled" );
+
+  if( NULL != mImpl->mEventData )
+  {
+    mImpl->mEventData->mCursorBlinkEnabled = enable;
+
+    if( !enable &&
+        mImpl->mEventData->mDecorator )
+    {
+      mImpl->mEventData->mDecorator->StopCursorBlink();
+    }
+  }
+}
+
+bool Controller::GetEnableCursorBlink() const
+{
+  if( NULL != mImpl->mEventData )
+  {
+    return mImpl->mEventData->mCursorBlinkEnabled;
+  }
+
+  return false;
+}
+
+void Controller::SetMultiLineEnabled( bool enable )
+{
+  const LayoutEngine::Layout layout = enable ? LayoutEngine::MULTI_LINE_BOX : LayoutEngine::SINGLE_LINE_BOX;
+
+  if( layout != mImpl->mLayoutEngine.GetLayout() )
+  {
+    // Set the layout type.
+    mImpl->mLayoutEngine.SetLayout( layout );
+
+    // Set the flags to redo the layout operations
+    const OperationsMask layoutOperations =  static_cast<OperationsMask>( LAYOUT             |
+                                                                          UPDATE_LAYOUT_SIZE |
+                                                                          ALIGN              |
+                                                                          REORDER );
+
+    mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
+
+    mImpl->RequestRelayout();
+  }
+}
+
+bool Controller::IsMultiLineEnabled() const
+{
+  return LayoutEngine::MULTI_LINE_BOX == mImpl->mLayoutEngine.GetLayout();
+}
+
+void Controller::SetHorizontalAlignment( LayoutEngine::HorizontalAlignment alignment )
+{
+  if( alignment != mImpl->mLayoutEngine.GetHorizontalAlignment() )
+  {
+    // Set the alignment.
+    mImpl->mLayoutEngine.SetHorizontalAlignment( alignment );
+
+    // Set the flag to redo the alignment operation.
+    mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
+
+    mImpl->RequestRelayout();
+  }
+}
+
+LayoutEngine::HorizontalAlignment Controller::GetHorizontalAlignment() const
+{
+  return mImpl->mLayoutEngine.GetHorizontalAlignment();
+}
+
+void Controller::SetVerticalAlignment( LayoutEngine::VerticalAlignment alignment )
+{
+  if( alignment != mImpl->mLayoutEngine.GetVerticalAlignment() )
+  {
+    // Set the alignment.
+    mImpl->mLayoutEngine.SetVerticalAlignment( alignment );
+
+    mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
+
+    mImpl->RequestRelayout();
+  }
+}
+
+LayoutEngine::VerticalAlignment Controller::GetVerticalAlignment() const
+{
+  return mImpl->mLayoutEngine.GetVerticalAlignment();
+}
+
+// public : Update
+
 void Controller::SetText( const std::string& text )
 {
   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText\n" );
@@ -409,16 +514,23 @@ void Controller::GetPlaceholderText( PlaceholderType type, std::string& text ) c
   }
 }
 
-void Controller::SetMaximumNumberOfCharacters( Length maxCharacters )
+void Controller::UpdateAfterFontChange( const std::string& newDefaultFont )
 {
-  mImpl->mMaximumNumberOfCharacters = maxCharacters;
-}
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::UpdateAfterFontChange\n");
 
-int Controller::GetMaximumNumberOfCharacters()
-{
-  return mImpl->mMaximumNumberOfCharacters;
+  if( !mImpl->mFontDefaults->familyDefined ) // If user defined font then should not update when system font changes
+  {
+    DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange newDefaultFont(%s)\n", newDefaultFont.c_str() );
+    mImpl->mFontDefaults->mFontDescription.family = newDefaultFont;
+
+    ClearFontData();
+
+    mImpl->RequestRelayout();
+  }
 }
 
+// public : Default style & Input style
+
 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
 {
   if( NULL == mImpl->mFontDefaults )
@@ -565,21 +677,6 @@ float Controller::GetDefaultPointSize() const
   return 0.0f;
 }
 
-void Controller::UpdateAfterFontChange( const std::string& newDefaultFont )
-{
-  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::UpdateAfterFontChange\n");
-
-  if( !mImpl->mFontDefaults->familyDefined ) // If user defined font then should not update when system font changes
-  {
-    DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange newDefaultFont(%s)\n", newDefaultFont.c_str() );
-    mImpl->mFontDefaults->mFontDescription.family = newDefaultFont;
-
-    ClearFontData();
-
-    mImpl->RequestRelayout();
-  }
-}
-
 void Controller::SetTextColor( const Vector4& textColor )
 {
   mImpl->mTextColor = textColor;
@@ -597,92 +694,6 @@ const Vector4& Controller::GetTextColor() const
   return mImpl->mTextColor;
 }
 
-bool Controller::RemoveText( int cursorOffset,
-                             int numberOfCharacters,
-                             UpdateInputStyleType type )
-{
-  bool removed = false;
-
-  if( NULL == mImpl->mEventData )
-  {
-    return removed;
-  }
-
-  DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfCharacters %d\n",
-                 this, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition, cursorOffset, numberOfCharacters );
-
-  if( !mImpl->IsShowingPlaceholderText() )
-  {
-    // Delete at current cursor position
-    Vector<Character>& currentText = mImpl->mLogicalModel->mText;
-    CharacterIndex& oldCursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
-
-    CharacterIndex cursorIndex = oldCursorIndex;
-
-    // Validate the cursor position & number of characters
-    if( static_cast< CharacterIndex >( std::abs( cursorOffset ) ) <= cursorIndex )
-    {
-      cursorIndex = oldCursorIndex + cursorOffset;
-    }
-
-    if( ( cursorIndex + numberOfCharacters ) > currentText.Count() )
-    {
-      numberOfCharacters = currentText.Count() - cursorIndex;
-    }
-
-    if( ( cursorIndex + numberOfCharacters ) <= mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters )
-    {
-      // Mark the paragraphs to be updated.
-      mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
-      mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove += numberOfCharacters;
-
-      // Update the input style and remove the text's style before removing the text.
-
-      if( UPDATE_INPUT_STYLE == type )
-      {
-        // Keep a copy of the current input style.
-        InputStyle currentInputStyle;
-        currentInputStyle.Copy( mImpl->mEventData->mInputStyle );
-
-        // Set first the default input style.
-        mImpl->RetrieveDefaultInputStyle( mImpl->mEventData->mInputStyle );
-
-        // Update the input style.
-        mImpl->mLogicalModel->RetrieveStyle( cursorIndex, mImpl->mEventData->mInputStyle );
-
-        // Compare if the input style has changed.
-        const bool hasInputStyleChanged = !currentInputStyle.Equal( mImpl->mEventData->mInputStyle );
-
-        if( hasInputStyleChanged )
-        {
-          const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mImpl->mEventData->mInputStyle );
-          // Queue the input style changed signal.
-          mImpl->mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
-        }
-      }
-
-      // Updates the text style runs by removing characters. Runs with no characters are removed.
-      mImpl->mLogicalModel->UpdateTextStyleRuns( cursorIndex, -numberOfCharacters );
-
-      // Remove the characters.
-      Vector<Character>::Iterator first = currentText.Begin() + cursorIndex;
-      Vector<Character>::Iterator last  = first + numberOfCharacters;
-
-      currentText.Erase( first, last );
-
-      // Cursor position retreat
-      oldCursorIndex = cursorIndex;
-
-      mImpl->mEventData->mScrollAfterDelete = true;
-
-      DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", this, numberOfCharacters );
-      removed = true;
-    }
-  }
-
-  return removed;
-}
-
 void Controller::SetPlaceholderTextColor( const Vector4& textColor )
 {
   if( NULL != mImpl->mEventData )
@@ -1308,30 +1319,16 @@ const std::string& Controller::GetInputOutlineProperties() const
   return GetDefaultOutlineProperties();
 }
 
-void Controller::SetEnableCursorBlink( bool enable )
-{
-  DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "TextInput disabled" );
-
-  if( NULL != mImpl->mEventData )
-  {
-    mImpl->mEventData->mCursorBlinkEnabled = enable;
+// public : Queries & retrieves.
 
-    if( !enable &&
-        mImpl->mEventData->mDecorator )
-    {
-      mImpl->mEventData->mDecorator->StopCursorBlink();
-    }
-  }
+LayoutEngine& Controller::GetLayoutEngine()
+{
+  return mImpl->mLayoutEngine;
 }
 
-bool Controller::GetEnableCursorBlink() const
+View& Controller::GetView()
 {
-  if( NULL != mImpl->mEventData )
-  {
-    return mImpl->mEventData->mCursorBlinkEnabled;
-  }
-
-  return false;
+  return mImpl->mView;
 }
 
 const Vector2& Controller::GetScrollPosition() const
@@ -1476,6 +1473,8 @@ float Controller::GetHeightForWidth( float width )
   return layoutSize.height;
 }
 
+// public : Relayout.
+
 Controller::UpdateTextType Controller::Relayout( const Size& size )
 {
   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f, autoScroll[%s]\n", this, size.width, size.height, (mImpl->mAutoScrollEnabled)?"true":"false"  );
@@ -1584,58 +1583,12 @@ Controller::UpdateTextType Controller::Relayout( const Size& size )
   return updateTextType;
 }
 
-void Controller::ProcessModifyEvents()
+// public : Input style change signals.
+
+bool Controller::IsInputStyleChangedSignalsQueueEmpty()
 {
-  Vector<ModifyEvent>& events = mImpl->mModifyEvents;
-
-  if( 0u == events.Count() )
-  {
-    // Nothing to do.
-    return;
-  }
-
-  for( Vector<ModifyEvent>::ConstIterator it = events.Begin(),
-         endIt = events.End();
-       it != endIt;
-       ++it )
-  {
-    const ModifyEvent& event = *it;
-
-    if( ModifyEvent::TEXT_REPLACED == event.type )
-    {
-      // A (single) replace event should come first, otherwise we wasted time processing NOOP events
-      DALI_ASSERT_DEBUG( it == events.Begin() && "Unexpected TEXT_REPLACED event" );
-
-      TextReplacedEvent();
-    }
-    else if( ModifyEvent::TEXT_INSERTED == event.type )
-    {
-      TextInsertedEvent();
-    }
-    else if( ModifyEvent::TEXT_DELETED == event.type )
-    {
-      // Placeholder-text cannot be deleted
-      if( !mImpl->IsShowingPlaceholderText() )
-      {
-        TextDeletedEvent();
-      }
-    }
-  }
-
-  if( NULL != mImpl->mEventData )
-  {
-    // When the text is being modified, delay cursor blinking
-    mImpl->mEventData->mDecorator->DelayCursorBlink();
-  }
-
-  // Discard temporary text
-  events.Clear();
-}
-
-bool Controller::IsInputStyleChangedSignalsQueueEmpty()
-{
-  return ( NULL == mImpl->mEventData ) || ( 0u == mImpl->mEventData->mInputStyleChangedQueue.Count() );
-}
+  return ( NULL == mImpl->mEventData ) || ( 0u == mImpl->mEventData->mInputStyleChangedQueue.Count() );
+}
 
 void Controller::ProcessInputStyleChangedSignals()
 {
@@ -1659,496 +1612,531 @@ void Controller::ProcessInputStyleChangedSignals()
   mImpl->mEventData->mInputStyleChangedQueue.Clear();
 }
 
-void Controller::ResetText()
-{
-  // Reset buffers.
-  mImpl->mLogicalModel->mText.Clear();
-
-  // We have cleared everything including the placeholder-text
-  mImpl->PlaceholderCleared();
-
-  mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
-  mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
-  mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = 0u;
-
-  // Clear any previous text.
-  mImpl->mTextUpdateInfo.mClearAll = true;
-
-  // The natural size needs to be re-calculated.
-  mImpl->mRecalculateNaturalSize = true;
-
-  // Apply modifications to the model
-  mImpl->mOperationsPending = ALL_OPERATIONS;
-}
+// public : Text-input Event Queuing.
 
-void Controller::ResetCursorPosition( CharacterIndex cursorIndex )
+void Controller::KeyboardFocusGainEvent()
 {
-  // Reset the cursor position
+  DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
+
   if( NULL != mImpl->mEventData )
   {
-    mImpl->mEventData->mPrimaryCursorPosition = cursorIndex;
-
-    // Update the cursor if it's in editing mode.
-    if( EventData::IsEditingState( mImpl->mEventData->mState )  )
+    if( ( EventData::INACTIVE == mImpl->mEventData->mState ) ||
+        ( EventData::INTERRUPTED == mImpl->mEventData->mState ) )
     {
-      mImpl->mEventData->mUpdateCursorPosition = true;
+      mImpl->ChangeState( EventData::EDITING );
+      mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered.
+    }
+    mImpl->NotifyImfMultiLineStatus();
+    if( mImpl->IsShowingPlaceholderText() )
+    {
+      // Show alternative placeholder-text when editing
+      ShowPlaceholderText();
     }
+
+    mImpl->RequestRelayout();
   }
 }
 
-void Controller::ResetScrollPosition()
+void Controller::KeyboardFocusLostEvent()
 {
+  DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
+
   if( NULL != mImpl->mEventData )
   {
-    // Reset the scroll position.
-    mImpl->mScrollPosition = Vector2::ZERO;
-    mImpl->mEventData->mScrollAfterUpdatePosition = true;
+    if( EventData::INTERRUPTED != mImpl->mEventData->mState )
+    {
+      mImpl->ChangeState( EventData::INACTIVE );
+
+      if( !mImpl->IsShowingRealText() )
+      {
+        // Revert to regular placeholder-text when not editing
+        ShowPlaceholderText();
+      }
+    }
   }
+  mImpl->RequestRelayout();
 }
 
-void Controller::TextReplacedEvent()
+bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
 {
-  // The natural size needs to be re-calculated.
-  mImpl->mRecalculateNaturalSize = true;
-
-  // Apply modifications to the model
-  mImpl->mOperationsPending = ALL_OPERATIONS;
-}
+  DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
 
-void Controller::TextInsertedEvent()
-{
-  DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextInsertedEvent" );
+  bool textChanged( false );
 
-  if( NULL == mImpl->mEventData )
+  if( ( NULL != mImpl->mEventData ) &&
+      ( keyEvent.state == KeyEvent::Down ) )
   {
-    return;
-  }
+    int keyCode = keyEvent.keyCode;
+    const std::string& keyString = keyEvent.keyPressed;
 
-  // The natural size needs to be re-calculated.
-  mImpl->mRecalculateNaturalSize = true;
+    // Pre-process to separate modifying events from non-modifying input events.
+    if( Dali::DALI_KEY_ESCAPE == keyCode )
+    {
+      // Escape key is a special case which causes focus loss
+      KeyboardFocusLostEvent();
+    }
+    else if( ( Dali::DALI_KEY_CURSOR_LEFT  == keyCode ) ||
+             ( Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) ||
+             ( Dali::DALI_KEY_CURSOR_UP    == keyCode ) ||
+             ( Dali::DALI_KEY_CURSOR_DOWN  == keyCode ) )
+    {
+      Event event( Event::CURSOR_KEY_EVENT );
+      event.p1.mInt = keyCode;
+      mImpl->mEventData->mEventQueue.push_back( event );
+    }
+    else if( Dali::DALI_KEY_BACKSPACE == keyCode )
+    {
+      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 if( Dali::DALI_KEY_SHIFT_LEFT == keyCode )
+    {
+      // DALI_KEY_SHIFT_LEFT is the key code for the Left Shift. It's sent (by the imf?) when the predictive text is enabled
+      // and a character is typed after the type of a upper case latin character.
 
-  // Apply modifications to the model; TODO - Optimize this
-  mImpl->mOperationsPending = ALL_OPERATIONS;
-}
+      // Do nothing.
+    }
+    else
+    {
+      DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
 
-void Controller::TextDeletedEvent()
-{
-  DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextDeletedEvent" );
+      // IMF manager is no longer handling key-events
+      mImpl->ClearPreEditFlag();
 
-  if( NULL == mImpl->mEventData )
-  {
-    return;
+      InsertText( keyString, COMMIT );
+      textChanged = true;
+    }
+
+    if ( ( mImpl->mEventData->mState != EventData::INTERRUPTED ) &&
+         ( mImpl->mEventData->mState != EventData::INACTIVE ) &&
+         ( Dali::DALI_KEY_SHIFT_LEFT != keyCode ) )
+    {
+      // Should not change the state if the key is the shift send by the imf manager.
+      // Otherwise, when the state is SELECTING the text controller can't send the right
+      // surrounding info to the imf.
+      mImpl->ChangeState( EventData::EDITING );
+    }
+
+    mImpl->RequestRelayout();
   }
 
-  // The natural size needs to be re-calculated.
-  mImpl->mRecalculateNaturalSize = true;
+  if( textChanged )
+  {
+    // Do this last since it provides callbacks into application code
+    mImpl->mControlInterface.TextChanged();
+  }
 
-  // Apply modifications to the model; TODO - Optimize this
-  mImpl->mOperationsPending = ALL_OPERATIONS;
+  return true;
 }
 
-bool Controller::DoRelayout( const Size& size,
-                             OperationsMask operationsRequired,
-                             Size& layoutSize )
+void Controller::TapEvent( unsigned int tapCount, float x, float y )
 {
-  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout %p size %f,%f\n", this, size.width, size.height );
-  bool viewUpdated( false );
-
-  // Calculate the operations to be done.
-  const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
-
-  const CharacterIndex startIndex = mImpl->mTextUpdateInfo.mParagraphCharacterIndex;
-  const Length requestedNumberOfCharacters = mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters;
-
-  // Get the current layout size.
-  layoutSize = mImpl->mVisualModel->GetLayoutSize();
+  DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
 
-  if( NO_OPERATION != ( LAYOUT & operations ) )
+  if( NULL != mImpl->mEventData )
   {
-    DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout LAYOUT & operations\n");
-
-    // Some vectors with data needed to layout and reorder may be void
-    // after the first time the text has been laid out.
-    // Fill the vectors again.
-
-    // Calculate the number of glyphs to layout.
-    const Vector<GlyphIndex>& charactersToGlyph = mImpl->mVisualModel->mCharactersToGlyph;
-    const Vector<Length>& glyphsPerCharacter = mImpl->mVisualModel->mGlyphsPerCharacter;
-    const GlyphIndex* const charactersToGlyphBuffer = charactersToGlyph.Begin();
-    const Length* const glyphsPerCharacterBuffer = glyphsPerCharacter.Begin();
-
-    const CharacterIndex lastIndex = startIndex + ( ( requestedNumberOfCharacters > 0u ) ? requestedNumberOfCharacters - 1u : 0u );
-    const GlyphIndex startGlyphIndex = mImpl->mTextUpdateInfo.mStartGlyphIndex;
-    const Length numberOfGlyphs = ( requestedNumberOfCharacters > 0u ) ? *( charactersToGlyphBuffer + lastIndex ) + *( glyphsPerCharacterBuffer + lastIndex ) - startGlyphIndex : 0u;
-    const Length totalNumberOfGlyphs = mImpl->mVisualModel->mGlyphs.Count();
+    DALI_LOG_INFO( gLogFilter, Debug::Concise, "TapEvent state:%d \n", mImpl->mEventData->mState );
+    EventData::State state( mImpl->mEventData->mState );
+    bool relayoutNeeded( false );   // to avoid unnecessary relayouts when tapping an empty text-field
 
-    if( 0u == totalNumberOfGlyphs )
+    if( mImpl->IsClipboardVisible() )
     {
-      if( NO_OPERATION != ( UPDATE_LAYOUT_SIZE & operations ) )
+      if( EventData::INACTIVE == state || EventData::EDITING == state)
       {
-        mImpl->mVisualModel->SetLayoutSize( Size::ZERO );
+        mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
       }
-
-      // Nothing else to do if there is no glyphs.
-      DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout no glyphs, view updated true\n" );
-      return true;
+      relayoutNeeded = true;
     }
-
-    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,
-                                       textBuffer,
-                                       lineBreakInfo.Begin(),
-                                       wordBreakInfo.Begin(),
-                                       ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
-                                       glyphs.Begin(),
-                                       glyphsToCharactersMap.Begin(),
-                                       charactersPerGlyph.Begin(),
-                                       charactersToGlyphBuffer,
-                                       glyphsPerCharacterBuffer,
-                                       totalNumberOfGlyphs );
-
-    // Resize the vector of positions to have the same size than the vector of glyphs.
-    Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
-    glyphPositions.Resize( totalNumberOfGlyphs );
-
-    // Whether the last character is a new paragraph character.
-    mImpl->mTextUpdateInfo.mIsLastCharacterNewParagraph =  TextAbstraction::IsNewParagraph( *( textBuffer + ( mImpl->mLogicalModel->mText.Count() - 1u ) ) );
-    layoutParameters.isLastNewParagraph = mImpl->mTextUpdateInfo.mIsLastCharacterNewParagraph;
-
-    // The initial glyph and the number of glyphs to layout.
-    layoutParameters.startGlyphIndex = startGlyphIndex;
-    layoutParameters.numberOfGlyphs = numberOfGlyphs;
-    layoutParameters.startLineIndex = mImpl->mTextUpdateInfo.mStartLineIndex;
-    layoutParameters.estimatedNumberOfLines = mImpl->mTextUpdateInfo.mEstimatedNumberOfLines;
-
-    // Update the visual model.
-    Size newLayoutSize;
-    viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
-                                                   glyphPositions,
-                                                   mImpl->mVisualModel->mLines,
-                                                   newLayoutSize );
-
-    viewUpdated = viewUpdated || ( newLayoutSize != layoutSize );
-
-    if( viewUpdated )
+    else if( 1u == tapCount )
     {
-      layoutSize = newLayoutSize;
-
-      if ( NO_OPERATION != ( UPDATE_DIRECTION & operations ) )
+      if( EventData::EDITING_WITH_POPUP == state || EventData::EDITING_WITH_PASTE_POPUP == state )
       {
-        mImpl->mAutoScrollDirectionRTL = false;
+        mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );  // If Popup shown hide it here so can be shown again if required.
       }
 
-      // Reorder the lines
-      if( NO_OPERATION != ( REORDER & operations ) )
+      if( mImpl->IsShowingRealText() && ( EventData::INACTIVE != state ) )
       {
-        Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
-        Vector<BidirectionalLineInfoRun>& bidirectionalLineInfo = mImpl->mLogicalModel->mBidirectionalLineInfo;
-
-        // Check first if there are paragraphs with bidirectional info.
-        if( 0u != bidirectionalInfo.Count() )
+        mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
+        relayoutNeeded = true;
+      }
+      else
+      {
+        if( mImpl->IsShowingPlaceholderText() && !mImpl->IsFocusedPlaceholderAvailable() )
         {
-          // Get the lines
-          const Length numberOfLines = mImpl->mVisualModel->mLines.Count();
-
-          // Reorder the lines.
-          bidirectionalLineInfo.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
-          ReorderLines( bidirectionalInfo,
-                        startIndex,
-                        requestedNumberOfCharacters,
-                        mImpl->mVisualModel->mLines,
-                        bidirectionalLineInfo );
-
-          // Set the bidirectional info per line into the layout parameters.
-          layoutParameters.lineBidirectionalInfoRunsBuffer = bidirectionalLineInfo.Begin();
-          layoutParameters.numberOfBidirectionalInfoRuns = bidirectionalLineInfo.Count();
-
-          // Re-layout the text. Reorder those lines with right to left characters.
-          mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
-                                                         startIndex,
-                                                         requestedNumberOfCharacters,
-                                                         glyphPositions );
-
-          if ( ( NO_OPERATION != ( UPDATE_DIRECTION & operations ) ) && ( numberOfLines > 0 ) )
-          {
-            const LineRun* const firstline = mImpl->mVisualModel->mLines.Begin();
-            if ( firstline )
-            {
-              mImpl->mAutoScrollDirectionRTL = firstline->direction;
-            }
-          }
+          // Hide placeholder text
+          ResetText();
         }
-      } // REORDER
 
-      // Sets the layout size.
-      if( NO_OPERATION != ( UPDATE_LAYOUT_SIZE & operations ) )
+        if( EventData::INACTIVE == state )
+        {
+          mImpl->ChangeState( EventData::EDITING );
+        }
+        else if( !mImpl->IsClipboardEmpty() )
+        {
+          mImpl->ChangeState( EventData::EDITING_WITH_POPUP );
+        }
+        relayoutNeeded = true;
+      }
+    }
+    else if( 2u == tapCount )
+    {
+      if( mImpl->mEventData->mSelectionEnabled &&
+          mImpl->IsShowingRealText() )
       {
-        mImpl->mVisualModel->SetLayoutSize( layoutSize );
+        SelectEvent( x, y, false );
       }
-    } // view updated
+    }
+    // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
+    if( relayoutNeeded )
+    {
+      Event event( Event::TAP_EVENT );
+      event.p1.mUint = tapCount;
+      event.p2.mFloat = x;
+      event.p3.mFloat = y;
+      mImpl->mEventData->mEventQueue.push_back( event );
 
-    // Store the size used to layout the text.
-    mImpl->mVisualModel->mControlSize = size;
+      mImpl->RequestRelayout();
+    }
   }
 
-  if( NO_OPERATION != ( ALIGN & operations ) )
-  {
-    // The laid-out lines.
-    Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
-
-    mImpl->mLayoutEngine.Align( size,
-                                startIndex,
-                                requestedNumberOfCharacters,
-                                lines );
-
-    viewUpdated = true;
-  }
-#if defined(DEBUG_ENABLED)
-  std::string currentText;
-  GetText( currentText );
-  DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::DoRelayout [%p] mImpl->mAutoScrollDirectionRTL[%s] [%s]\n", this, (mImpl->mAutoScrollDirectionRTL)?"true":"false",  currentText.c_str() );
-#endif
-  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
-  return viewUpdated;
+  // Reset keyboard as tap event has occurred.
+  mImpl->ResetImfManager();
 }
 
-void Controller::SetMultiLineEnabled( bool enable )
+void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
 {
-  const LayoutEngine::Layout layout = enable ? LayoutEngine::MULTI_LINE_BOX : LayoutEngine::SINGLE_LINE_BOX;
+  DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
 
-  if( layout != mImpl->mLayoutEngine.GetLayout() )
+  if( NULL != mImpl->mEventData )
   {
-    // Set the layout type.
-    mImpl->mLayoutEngine.SetLayout( layout );
-
-    // Set the flags to redo the layout operations
-    const OperationsMask layoutOperations =  static_cast<OperationsMask>( LAYOUT             |
-                                                                          UPDATE_LAYOUT_SIZE |
-                                                                          ALIGN              |
-                                                                          REORDER );
-
-    mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
+    Event event( Event::PAN_EVENT );
+    event.p1.mInt = state;
+    event.p2.mFloat = displacement.x;
+    event.p3.mFloat = displacement.y;
+    mImpl->mEventData->mEventQueue.push_back( event );
 
     mImpl->RequestRelayout();
   }
 }
 
-bool Controller::IsMultiLineEnabled() const
+void Controller::LongPressEvent( Gesture::State state, float x, float y  )
 {
-  return LayoutEngine::MULTI_LINE_BOX == mImpl->mLayoutEngine.GetLayout();
-}
+  DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected LongPressEvent" );
 
-void Controller::SetHorizontalAlignment( LayoutEngine::HorizontalAlignment alignment )
-{
-  if( alignment != mImpl->mLayoutEngine.GetHorizontalAlignment() )
+  if( ( state == Gesture::Started ) &&
+      ( NULL != mImpl->mEventData ) )
   {
-    // Set the alignment.
-    mImpl->mLayoutEngine.SetHorizontalAlignment( alignment );
-
-    // Set the flag to redo the alignment operation.
-    mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
-
-    mImpl->RequestRelayout();
-  }
-}
-
-LayoutEngine::HorizontalAlignment Controller::GetHorizontalAlignment() const
-{
-  return mImpl->mLayoutEngine.GetHorizontalAlignment();
-}
+    if( !mImpl->IsShowingRealText() )
+    {
+      Event event( Event::LONG_PRESS_EVENT );
+      event.p1.mInt = state;
+      mImpl->mEventData->mEventQueue.push_back( event );
+      mImpl->RequestRelayout();
+    }
+    else
+    {
+      // The 1st long-press on inactive text-field is treated as tap
+      if( EventData::INACTIVE == mImpl->mEventData->mState )
+      {
+        mImpl->ChangeState( EventData::EDITING );
 
-void Controller::SetVerticalAlignment( LayoutEngine::VerticalAlignment alignment )
-{
-  if( alignment != mImpl->mLayoutEngine.GetVerticalAlignment() )
-  {
-    // Set the alignment.
-    mImpl->mLayoutEngine.SetVerticalAlignment( alignment );
+        Event event( Event::TAP_EVENT );
+        event.p1.mUint = 1;
+        event.p2.mFloat = x;
+        event.p3.mFloat = y;
+        mImpl->mEventData->mEventQueue.push_back( event );
 
-    mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
+        mImpl->RequestRelayout();
+      }
+      else if( !mImpl->IsClipboardVisible() )
+      {
+        // Reset the imf manger to commit the pre-edit before selecting the text.
+        mImpl->ResetImfManager();
 
-    mImpl->RequestRelayout();
+        SelectEvent( x, y, false );
+      }
+    }
   }
 }
 
-LayoutEngine::VerticalAlignment Controller::GetVerticalAlignment() const
-{
-  return mImpl->mLayoutEngine.GetVerticalAlignment();
-}
-
-void Controller::CalculateVerticalOffset( const Size& controlSize )
+ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
 {
-  Size layoutSize = mImpl->mVisualModel->GetLayoutSize();
+  // Whether the text needs to be relaid-out.
+  bool requestRelayout = false;
 
-  if( fabsf( layoutSize.height ) < Math::MACHINE_EPSILON_1000 )
-  {
-    // Get the line height of the default font.
-    layoutSize.height = mImpl->GetDefaultFontLineHeight();
-  }
+  // Whether to retrieve the text and cursor position to be sent to the IMF manager.
+  bool retrieveText = false;
+  bool retrieveCursor = false;
 
-  switch( mImpl->mLayoutEngine.GetVerticalAlignment() )
+  switch( imfEvent.eventName )
   {
-    case LayoutEngine::VERTICAL_ALIGN_TOP:
+    case ImfManager::COMMIT:
     {
-      mImpl->mScrollPosition.y = 0.f;
+      InsertText( imfEvent.predictiveString, Text::Controller::COMMIT );
+      requestRelayout = true;
+      retrieveCursor = true;
       break;
     }
-    case LayoutEngine::VERTICAL_ALIGN_CENTER:
+    case ImfManager::PREEDIT:
     {
-      mImpl->mScrollPosition.y = floorf( 0.5f * ( controlSize.height - layoutSize.height ) ); // try to avoid pixel alignment.
+      InsertText( imfEvent.predictiveString, Text::Controller::PRE_EDIT );
+      requestRelayout = true;
+      retrieveCursor = true;
       break;
     }
-    case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
+    case ImfManager::DELETESURROUNDING:
     {
-      mImpl->mScrollPosition.y = controlSize.height - layoutSize.height;
+      const bool textDeleted = RemoveText( imfEvent.cursorOffset,
+                                           imfEvent.numberOfChars,
+                                           DONT_UPDATE_INPUT_STYLE );
+
+      if( textDeleted )
+      {
+        if( ( 0u != mImpl->mLogicalModel->mText.Count() ) ||
+            !mImpl->IsPlaceholderAvailable() )
+        {
+          mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
+        }
+        else
+        {
+          ShowPlaceholderText();
+        }
+        mImpl->mEventData->mUpdateCursorPosition = true;
+        mImpl->mEventData->mScrollAfterDelete = true;
+
+        requestRelayout = true;
+      }
+      break;
+    }
+    case ImfManager::GETSURROUNDING:
+    {
+      retrieveText = true;
+      retrieveCursor = true;
+      break;
+    }
+    case ImfManager::VOID:
+    {
+      // do nothing
       break;
     }
+  } // end switch
+
+  if( requestRelayout )
+  {
+    mImpl->mOperationsPending = ALL_OPERATIONS;
+    mImpl->RequestRelayout();
+  }
+
+  std::string text;
+  CharacterIndex cursorPosition = 0u;
+  Length numberOfWhiteSpaces = 0u;
+
+  if( retrieveCursor )
+  {
+    numberOfWhiteSpaces = mImpl->GetNumberOfWhiteSpaces( 0u );
+
+    cursorPosition = mImpl->GetLogicalCursorPosition();
+
+    if( cursorPosition < numberOfWhiteSpaces )
+    {
+      cursorPosition = 0u;
+    }
+    else
+    {
+      cursorPosition -= numberOfWhiteSpaces;
+    }
+  }
+
+  if( retrieveText )
+  {
+    mImpl->GetText( numberOfWhiteSpaces, text );
+  }
+
+  ImfManager::ImfCallbackData callbackData( ( retrieveText || retrieveCursor ), cursorPosition, text, false );
+
+  if( requestRelayout )
+  {
+    // Do this last since it provides callbacks into application code
+    mImpl->mControlInterface.TextChanged();
   }
+
+  return callbackData;
 }
 
-LayoutEngine& Controller::GetLayoutEngine()
+void Controller::PasteClipboardItemEvent()
 {
-  return mImpl->mLayoutEngine;
+  // Retrieve the clipboard contents first
+  ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
+  std::string stringToPaste( notifier.GetContent() );
+
+  // Commit the current pre-edit text; the contents of the clipboard should be appended
+  mImpl->ResetImfManager();
+
+  // Temporary disable hiding clipboard
+  mImpl->SetClipboardHideEnable( false );
+
+  // Paste
+  PasteText( stringToPaste );
+
+  mImpl->SetClipboardHideEnable( true );
 }
 
-View& Controller::GetView()
+// protected : Inherit from Text::Decorator::ControllerInterface.
+
+void Controller::GetTargetSize( Vector2& targetSize )
 {
-  return mImpl->mView;
+  targetSize = mImpl->mVisualModel->mControlSize;
 }
 
-void Controller::KeyboardFocusGainEvent()
+void Controller::AddDecoration( Actor& actor, bool needsClipping )
 {
-  DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
+  mImpl->mControlInterface.AddDecoration( actor, needsClipping );
+}
+
+void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
+{
+  DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected DecorationEvent" );
 
   if( NULL != mImpl->mEventData )
   {
-    if( ( EventData::INACTIVE == mImpl->mEventData->mState ) ||
-        ( EventData::INTERRUPTED == mImpl->mEventData->mState ) )
-    {
-      mImpl->ChangeState( EventData::EDITING );
-      mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered.
-    }
-    mImpl->NotifyImfMultiLineStatus();
-    if( mImpl->IsShowingPlaceholderText() )
+    switch( handleType )
     {
-      // Show alternative placeholder-text when editing
-      ShowPlaceholderText();
+      case GRAB_HANDLE:
+      {
+        Event event( Event::GRAB_HANDLE_EVENT );
+        event.p1.mUint  = state;
+        event.p2.mFloat = x;
+        event.p3.mFloat = y;
+
+        mImpl->mEventData->mEventQueue.push_back( event );
+        break;
+      }
+      case LEFT_SELECTION_HANDLE:
+      {
+        Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
+        event.p1.mUint  = state;
+        event.p2.mFloat = x;
+        event.p3.mFloat = y;
+
+        mImpl->mEventData->mEventQueue.push_back( event );
+        break;
+      }
+      case RIGHT_SELECTION_HANDLE:
+      {
+        Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
+        event.p1.mUint  = state;
+        event.p2.mFloat = x;
+        event.p3.mFloat = y;
+
+        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" );
+      }
     }
 
     mImpl->RequestRelayout();
   }
 }
 
-void Controller::KeyboardFocusLostEvent()
+// protected : Inherit from TextSelectionPopup::TextPopupButtonCallbackInterface.
+
+void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button )
 {
-  DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
+  if( NULL == mImpl->mEventData )
+  {
+    return;
+  }
 
-  if( NULL != mImpl->mEventData )
+  switch( button )
   {
-    if( EventData::INTERRUPTED != mImpl->mEventData->mState )
+    case Toolkit::TextSelectionPopup::CUT:
     {
-      mImpl->ChangeState( EventData::INACTIVE );
+      mImpl->SendSelectionToClipboard( true ); // Synchronous call to modify text
+      mImpl->mOperationsPending = ALL_OPERATIONS;
 
-      if( !mImpl->IsShowingRealText() )
+      if( ( 0u != mImpl->mLogicalModel->mText.Count() ) ||
+          !mImpl->IsPlaceholderAvailable() )
+      {
+        mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
+      }
+      else
       {
-        // Revert to regular placeholder-text when not editing
         ShowPlaceholderText();
       }
-    }
-  }
-  mImpl->RequestRelayout();
-}
 
-bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
-{
-  DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
+      mImpl->mEventData->mUpdateCursorPosition = true;
+      mImpl->mEventData->mScrollAfterDelete = true;
 
-  bool textChanged( false );
+      mImpl->RequestRelayout();
+      mImpl->mControlInterface.TextChanged();
+      break;
+    }
+    case Toolkit::TextSelectionPopup::COPY:
+    {
+      mImpl->SendSelectionToClipboard( false ); // Text not modified
 
-  if( ( NULL != mImpl->mEventData ) &&
-      ( keyEvent.state == KeyEvent::Down ) )
-  {
-    int keyCode = keyEvent.keyCode;
-    const std::string& keyString = keyEvent.keyPressed;
+      mImpl->mEventData->mUpdateCursorPosition = true;
 
-    // Pre-process to separate modifying events from non-modifying input events.
-    if( Dali::DALI_KEY_ESCAPE == keyCode )
-    {
-      // Escape key is a special case which causes focus loss
-      KeyboardFocusLostEvent();
+      mImpl->RequestRelayout(); // Cursor, Handles, Selection Highlight, Popup
+      break;
     }
-    else if( ( Dali::DALI_KEY_CURSOR_LEFT  == keyCode ) ||
-             ( Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) ||
-             ( Dali::DALI_KEY_CURSOR_UP    == keyCode ) ||
-             ( Dali::DALI_KEY_CURSOR_DOWN  == keyCode ) )
+    case Toolkit::TextSelectionPopup::PASTE:
     {
-      Event event( Event::CURSOR_KEY_EVENT );
-      event.p1.mInt = keyCode;
-      mImpl->mEventData->mEventQueue.push_back( event );
+      mImpl->RequestGetTextFromClipboard(); // Request clipboard service to retrieve an item
+      break;
     }
-    else if( Dali::DALI_KEY_BACKSPACE == keyCode )
+    case Toolkit::TextSelectionPopup::SELECT:
     {
-      textChanged = BackspaceKeyEvent();
+      const Vector2& currentCursorPosition = mImpl->mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
+
+      if( mImpl->mEventData->mSelectionEnabled )
+      {
+        // Creates a SELECT event.
+        SelectEvent( currentCursorPosition.x, currentCursorPosition.y, false );
+      }
+      break;
     }
-    else if( IsKey( keyEvent,  Dali::DALI_KEY_POWER ) )
+    case Toolkit::TextSelectionPopup::SELECT_ALL:
     {
-      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
+      // Creates a SELECT_ALL event
+      SelectEvent( 0.f, 0.f, true );
+      break;
     }
-    else if( IsKey( keyEvent, Dali::DALI_KEY_MENU ) ||
-             IsKey( keyEvent, Dali::DALI_KEY_HOME ) )
+    case Toolkit::TextSelectionPopup::CLIPBOARD:
     {
-      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
+      mImpl->ShowClipboard();
+      break;
     }
-    else if( Dali::DALI_KEY_SHIFT_LEFT == keyCode )
+    case Toolkit::TextSelectionPopup::NONE:
     {
-      // DALI_KEY_SHIFT_LEFT is the key code for the Left Shift. It's sent (by the imf?) when the predictive text is enabled
-      // and a character is typed after the type of a upper case latin character.
-
-      // Do nothing.
-    }
-    else
-    {
-      DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
-
-      // IMF manager is no longer handling key-events
-      mImpl->ClearPreEditFlag();
-
-      InsertText( keyString, COMMIT );
-      textChanged = true;
-    }
-
-    if ( ( mImpl->mEventData->mState != EventData::INTERRUPTED ) &&
-         ( mImpl->mEventData->mState != EventData::INACTIVE ) &&
-         ( Dali::DALI_KEY_SHIFT_LEFT != keyCode ) )
-    {
-      // Should not change the state if the key is the shift send by the imf manager.
-      // Otherwise, when the state is SELECTING the text controller can't send the right
-      // surrounding info to the imf.
-      mImpl->ChangeState( EventData::EDITING );
+      // Nothing to do.
+      break;
     }
-
-    mImpl->RequestRelayout();
-  }
-
-  if( textChanged )
-  {
-    // Do this last since it provides callbacks into application code
-    mImpl->mControlInterface.TextChanged();
   }
-
-  return true;
 }
 
+// private : Update.
+
 void Controller::InsertText( const std::string& text, Controller::InsertType type )
 {
   bool removedPrevious( false );
@@ -2392,452 +2380,438 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ
   }
 }
 
-bool Controller::RemoveSelectedText()
+void Controller::PasteText( const std::string& stringToPaste )
 {
-  bool textRemoved( false );
-
-  if( EventData::SELECTING == mImpl->mEventData->mState )
-  {
-    std::string removedString;
-    mImpl->RetrieveSelection( removedString, true );
-
-    if( !removedString.empty() )
-    {
-      textRemoved = true;
-      mImpl->ChangeState( EventData::EDITING );
-    }
-  }
+  InsertText( stringToPaste, Text::Controller::COMMIT );
+  mImpl->ChangeState( EventData::EDITING );
+  mImpl->RequestRelayout();
 
-  return textRemoved;
+  // Do this last since it provides callbacks into application code
+  mImpl->mControlInterface.TextChanged();
 }
 
-void Controller::TapEvent( unsigned int tapCount, float x, float y )
+bool Controller::RemoveText( int cursorOffset,
+                             int numberOfCharacters,
+                             UpdateInputStyleType type )
 {
-  DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
+  bool removed = false;
 
-  if( NULL != mImpl->mEventData )
+  if( NULL == mImpl->mEventData )
   {
-    DALI_LOG_INFO( gLogFilter, Debug::Concise, "TapEvent state:%d \n", mImpl->mEventData->mState );
-    EventData::State state( mImpl->mEventData->mState );
-    bool relayoutNeeded( false );   // to avoid unnecessary relayouts when tapping an empty text-field
+    return removed;
+  }
 
-    if( mImpl->IsClipboardVisible() )
-    {
-      if( EventData::INACTIVE == state || EventData::EDITING == state)
-      {
-        mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
-      }
-      relayoutNeeded = true;
-    }
-    else if( 1u == tapCount )
-    {
-      if( EventData::EDITING_WITH_POPUP == state || EventData::EDITING_WITH_PASTE_POPUP == state )
-      {
-        mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );  // If Popup shown hide it here so can be shown again if required.
-      }
+  DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfCharacters %d\n",
+                 this, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition, cursorOffset, numberOfCharacters );
 
-      if( mImpl->IsShowingRealText() && ( EventData::INACTIVE != state ) )
-      {
-        mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
-        relayoutNeeded = true;
-      }
-      else
-      {
-        if( mImpl->IsShowingPlaceholderText() && !mImpl->IsFocusedPlaceholderAvailable() )
-        {
-          // Hide placeholder text
-          ResetText();
-        }
+  if( !mImpl->IsShowingPlaceholderText() )
+  {
+    // Delete at current cursor position
+    Vector<Character>& currentText = mImpl->mLogicalModel->mText;
+    CharacterIndex& oldCursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
 
-        if( EventData::INACTIVE == state )
-        {
-          mImpl->ChangeState( EventData::EDITING );
-        }
-        else if( !mImpl->IsClipboardEmpty() )
-        {
-          mImpl->ChangeState( EventData::EDITING_WITH_POPUP );
-        }
-        relayoutNeeded = true;
-      }
+    CharacterIndex cursorIndex = oldCursorIndex;
+
+    // Validate the cursor position & number of characters
+    if( static_cast< CharacterIndex >( std::abs( cursorOffset ) ) <= cursorIndex )
+    {
+      cursorIndex = oldCursorIndex + cursorOffset;
     }
-    else if( 2u == tapCount )
+
+    if( ( cursorIndex + numberOfCharacters ) > currentText.Count() )
     {
-      if( mImpl->mEventData->mSelectionEnabled &&
-          mImpl->IsShowingRealText() )
-      {
-        SelectEvent( x, y, false );
-      }
+      numberOfCharacters = currentText.Count() - cursorIndex;
     }
-    // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
-    if( relayoutNeeded )
+
+    if( ( cursorIndex + numberOfCharacters ) <= mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters )
     {
-      Event event( Event::TAP_EVENT );
-      event.p1.mUint = tapCount;
-      event.p2.mFloat = x;
-      event.p3.mFloat = y;
-      mImpl->mEventData->mEventQueue.push_back( event );
+      // Mark the paragraphs to be updated.
+      mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
+      mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove += numberOfCharacters;
 
-      mImpl->RequestRelayout();
-    }
-  }
+      // Update the input style and remove the text's style before removing the text.
 
-  // Reset keyboard as tap event has occurred.
-  mImpl->ResetImfManager();
-}
+      if( UPDATE_INPUT_STYLE == type )
+      {
+        // Keep a copy of the current input style.
+        InputStyle currentInputStyle;
+        currentInputStyle.Copy( mImpl->mEventData->mInputStyle );
 
-void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
-{
-  DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
+        // Set first the default input style.
+        mImpl->RetrieveDefaultInputStyle( mImpl->mEventData->mInputStyle );
 
-  if( NULL != mImpl->mEventData )
-  {
-    Event event( Event::PAN_EVENT );
-    event.p1.mInt = state;
-    event.p2.mFloat = displacement.x;
-    event.p3.mFloat = displacement.y;
-    mImpl->mEventData->mEventQueue.push_back( event );
+        // Update the input style.
+        mImpl->mLogicalModel->RetrieveStyle( cursorIndex, mImpl->mEventData->mInputStyle );
 
-    mImpl->RequestRelayout();
-  }
-}
+        // Compare if the input style has changed.
+        const bool hasInputStyleChanged = !currentInputStyle.Equal( mImpl->mEventData->mInputStyle );
 
-void Controller::LongPressEvent( Gesture::State state, float x, float y  )
-{
-  DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected LongPressEvent" );
+        if( hasInputStyleChanged )
+        {
+          const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mImpl->mEventData->mInputStyle );
+          // Queue the input style changed signal.
+          mImpl->mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask );
+        }
+      }
 
-  if( ( state == Gesture::Started ) &&
-      ( NULL != mImpl->mEventData ) )
-  {
-    if( !mImpl->IsShowingRealText() )
-    {
-      Event event( Event::LONG_PRESS_EVENT );
-      event.p1.mInt = state;
-      mImpl->mEventData->mEventQueue.push_back( event );
-      mImpl->RequestRelayout();
-    }
-    else
-    {
-      // The 1st long-press on inactive text-field is treated as tap
-      if( EventData::INACTIVE == mImpl->mEventData->mState )
-      {
-        mImpl->ChangeState( EventData::EDITING );
+      // Updates the text style runs by removing characters. Runs with no characters are removed.
+      mImpl->mLogicalModel->UpdateTextStyleRuns( cursorIndex, -numberOfCharacters );
 
-        Event event( Event::TAP_EVENT );
-        event.p1.mUint = 1;
-        event.p2.mFloat = x;
-        event.p3.mFloat = y;
-        mImpl->mEventData->mEventQueue.push_back( event );
+      // Remove the characters.
+      Vector<Character>::Iterator first = currentText.Begin() + cursorIndex;
+      Vector<Character>::Iterator last  = first + numberOfCharacters;
 
-        mImpl->RequestRelayout();
-      }
-      else if( !mImpl->IsClipboardVisible() )
-      {
-        // Reset the imf manger to commit the pre-edit before selecting the text.
-        mImpl->ResetImfManager();
+      currentText.Erase( first, last );
 
-        SelectEvent( x, y, false );
-      }
+      // Cursor position retreat
+      oldCursorIndex = cursorIndex;
+
+      mImpl->mEventData->mScrollAfterDelete = true;
+
+      DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", this, numberOfCharacters );
+      removed = true;
     }
   }
+
+  return removed;
 }
 
-void Controller::SelectEvent( float x, float y, bool selectAll )
+bool Controller::RemoveSelectedText()
 {
-  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SelectEvent\n" );
+  bool textRemoved( false );
 
-  if( NULL != mImpl->mEventData )
+  if( EventData::SELECTING == mImpl->mEventData->mState )
   {
-    if( selectAll )
-    {
-      Event event( Event::SELECT_ALL );
-      mImpl->mEventData->mEventQueue.push_back( event );
-    }
-    else
+    std::string removedString;
+    mImpl->RetrieveSelection( removedString, true );
+
+    if( !removedString.empty() )
     {
-      Event event( Event::SELECT );
-      event.p2.mFloat = x;
-      event.p3.mFloat = y;
-      mImpl->mEventData->mEventQueue.push_back( event );
+      textRemoved = true;
+      mImpl->ChangeState( EventData::EDITING );
     }
-
-    mImpl->RequestRelayout();
   }
-}
 
-void Controller::GetTargetSize( Vector2& targetSize )
-{
-  targetSize = mImpl->mVisualModel->mControlSize;
+  return textRemoved;
 }
 
-void Controller::AddDecoration( Actor& actor, bool needsClipping )
-{
-  mImpl->mControlInterface.AddDecoration( actor, needsClipping );
-}
+// private : Relayout.
 
-void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
+bool Controller::DoRelayout( const Size& size,
+                             OperationsMask operationsRequired,
+                             Size& layoutSize )
 {
-  DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected DecorationEvent" );
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout %p size %f,%f\n", this, size.width, size.height );
+  bool viewUpdated( false );
 
-  if( NULL != mImpl->mEventData )
+  // Calculate the operations to be done.
+  const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
+
+  const CharacterIndex startIndex = mImpl->mTextUpdateInfo.mParagraphCharacterIndex;
+  const Length requestedNumberOfCharacters = mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters;
+
+  // Get the current layout size.
+  layoutSize = mImpl->mVisualModel->GetLayoutSize();
+
+  if( NO_OPERATION != ( LAYOUT & operations ) )
   {
-    switch( handleType )
-    {
-      case GRAB_HANDLE:
-      {
-        Event event( Event::GRAB_HANDLE_EVENT );
-        event.p1.mUint  = state;
-        event.p2.mFloat = x;
-        event.p3.mFloat = y;
+    DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout LAYOUT & operations\n");
 
-        mImpl->mEventData->mEventQueue.push_back( event );
-        break;
-      }
-      case LEFT_SELECTION_HANDLE:
-      {
-        Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
-        event.p1.mUint  = state;
-        event.p2.mFloat = x;
-        event.p3.mFloat = y;
+    // Some vectors with data needed to layout and reorder may be void
+    // after the first time the text has been laid out.
+    // Fill the vectors again.
 
-        mImpl->mEventData->mEventQueue.push_back( event );
-        break;
-      }
-      case RIGHT_SELECTION_HANDLE:
-      {
-        Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
-        event.p1.mUint  = state;
-        event.p2.mFloat = x;
-        event.p3.mFloat = y;
+    // Calculate the number of glyphs to layout.
+    const Vector<GlyphIndex>& charactersToGlyph = mImpl->mVisualModel->mCharactersToGlyph;
+    const Vector<Length>& glyphsPerCharacter = mImpl->mVisualModel->mGlyphsPerCharacter;
+    const GlyphIndex* const charactersToGlyphBuffer = charactersToGlyph.Begin();
+    const Length* const glyphsPerCharacterBuffer = glyphsPerCharacter.Begin();
 
-        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:
+    const CharacterIndex lastIndex = startIndex + ( ( requestedNumberOfCharacters > 0u ) ? requestedNumberOfCharacters - 1u : 0u );
+    const GlyphIndex startGlyphIndex = mImpl->mTextUpdateInfo.mStartGlyphIndex;
+    const Length numberOfGlyphs = ( requestedNumberOfCharacters > 0u ) ? *( charactersToGlyphBuffer + lastIndex ) + *( glyphsPerCharacterBuffer + lastIndex ) - startGlyphIndex : 0u;
+    const Length totalNumberOfGlyphs = mImpl->mVisualModel->mGlyphs.Count();
+
+    if( 0u == totalNumberOfGlyphs )
+    {
+      if( NO_OPERATION != ( UPDATE_LAYOUT_SIZE & operations ) )
       {
-        DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
+        mImpl->mVisualModel->SetLayoutSize( Size::ZERO );
       }
-    }
 
-    mImpl->RequestRelayout();
-  }
-}
-
-void Controller::PasteText( const std::string& stringToPaste )
-{
-  InsertText( stringToPaste, Text::Controller::COMMIT );
-  mImpl->ChangeState( EventData::EDITING );
-  mImpl->RequestRelayout();
+      // Nothing else to do if there is no glyphs.
+      DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout no glyphs, view updated true\n" );
+      return true;
+    }
 
-  // Do this last since it provides callbacks into application code
-  mImpl->mControlInterface.TextChanged();
-}
+    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();
 
-void Controller::PasteClipboardItemEvent()
-{
-  // Retrieve the clipboard contents first
-  ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
-  std::string stringToPaste( notifier.GetContent() );
+    // Set the layout parameters.
+    LayoutParameters layoutParameters( size,
+                                       textBuffer,
+                                       lineBreakInfo.Begin(),
+                                       wordBreakInfo.Begin(),
+                                       ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
+                                       glyphs.Begin(),
+                                       glyphsToCharactersMap.Begin(),
+                                       charactersPerGlyph.Begin(),
+                                       charactersToGlyphBuffer,
+                                       glyphsPerCharacterBuffer,
+                                       totalNumberOfGlyphs );
 
-  // Commit the current pre-edit text; the contents of the clipboard should be appended
-  mImpl->ResetImfManager();
+    // Resize the vector of positions to have the same size than the vector of glyphs.
+    Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
+    glyphPositions.Resize( totalNumberOfGlyphs );
 
-  // Temporary disable hiding clipboard
-  mImpl->SetClipboardHideEnable( false );
+    // Whether the last character is a new paragraph character.
+    mImpl->mTextUpdateInfo.mIsLastCharacterNewParagraph =  TextAbstraction::IsNewParagraph( *( textBuffer + ( mImpl->mLogicalModel->mText.Count() - 1u ) ) );
+    layoutParameters.isLastNewParagraph = mImpl->mTextUpdateInfo.mIsLastCharacterNewParagraph;
 
-  // Paste
-  PasteText( stringToPaste );
+    // The initial glyph and the number of glyphs to layout.
+    layoutParameters.startGlyphIndex = startGlyphIndex;
+    layoutParameters.numberOfGlyphs = numberOfGlyphs;
+    layoutParameters.startLineIndex = mImpl->mTextUpdateInfo.mStartLineIndex;
+    layoutParameters.estimatedNumberOfLines = mImpl->mTextUpdateInfo.mEstimatedNumberOfLines;
 
-  mImpl->SetClipboardHideEnable( true );
-}
+    // Update the visual model.
+    Size newLayoutSize;
+    viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
+                                                   glyphPositions,
+                                                   mImpl->mVisualModel->mLines,
+                                                   newLayoutSize );
 
-void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button )
-{
-  if( NULL == mImpl->mEventData )
-  {
-    return;
-  }
+    viewUpdated = viewUpdated || ( newLayoutSize != layoutSize );
 
-  switch( button )
-  {
-    case Toolkit::TextSelectionPopup::CUT:
+    if( viewUpdated )
     {
-      mImpl->SendSelectionToClipboard( true ); // Synchronous call to modify text
-      mImpl->mOperationsPending = ALL_OPERATIONS;
+      layoutSize = newLayoutSize;
 
-      if( ( 0u != mImpl->mLogicalModel->mText.Count() ) ||
-          !mImpl->IsPlaceholderAvailable() )
+      if ( NO_OPERATION != ( UPDATE_DIRECTION & operations ) )
       {
-        mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
+        mImpl->mAutoScrollDirectionRTL = false;
       }
-      else
+
+      // Reorder the lines
+      if( NO_OPERATION != ( REORDER & operations ) )
       {
-        ShowPlaceholderText();
+        Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
+        Vector<BidirectionalLineInfoRun>& bidirectionalLineInfo = mImpl->mLogicalModel->mBidirectionalLineInfo;
+
+        // Check first if there are paragraphs with bidirectional info.
+        if( 0u != bidirectionalInfo.Count() )
+        {
+          // Get the lines
+          const Length numberOfLines = mImpl->mVisualModel->mLines.Count();
+
+          // Reorder the lines.
+          bidirectionalLineInfo.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
+          ReorderLines( bidirectionalInfo,
+                        startIndex,
+                        requestedNumberOfCharacters,
+                        mImpl->mVisualModel->mLines,
+                        bidirectionalLineInfo );
+
+          // Set the bidirectional info per line into the layout parameters.
+          layoutParameters.lineBidirectionalInfoRunsBuffer = bidirectionalLineInfo.Begin();
+          layoutParameters.numberOfBidirectionalInfoRuns = bidirectionalLineInfo.Count();
+
+          // Re-layout the text. Reorder those lines with right to left characters.
+          mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
+                                                         startIndex,
+                                                         requestedNumberOfCharacters,
+                                                         glyphPositions );
+
+          if ( ( NO_OPERATION != ( UPDATE_DIRECTION & operations ) ) && ( numberOfLines > 0 ) )
+          {
+            const LineRun* const firstline = mImpl->mVisualModel->mLines.Begin();
+            if ( firstline )
+            {
+              mImpl->mAutoScrollDirectionRTL = firstline->direction;
+            }
+          }
+        }
+      } // REORDER
+
+      // Sets the layout size.
+      if( NO_OPERATION != ( UPDATE_LAYOUT_SIZE & operations ) )
+      {
+        mImpl->mVisualModel->SetLayoutSize( layoutSize );
       }
+    } // view updated
 
-      mImpl->mEventData->mUpdateCursorPosition = true;
-      mImpl->mEventData->mScrollAfterDelete = true;
+    // Store the size used to layout the text.
+    mImpl->mVisualModel->mControlSize = size;
+  }
 
-      mImpl->RequestRelayout();
-      mImpl->mControlInterface.TextChanged();
-      break;
-    }
-    case Toolkit::TextSelectionPopup::COPY:
-    {
-      mImpl->SendSelectionToClipboard( false ); // Text not modified
+  if( NO_OPERATION != ( ALIGN & operations ) )
+  {
+    // The laid-out lines.
+    Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
+
+    mImpl->mLayoutEngine.Align( size,
+                                startIndex,
+                                requestedNumberOfCharacters,
+                                lines );
+
+    viewUpdated = true;
+  }
+#if defined(DEBUG_ENABLED)
+  std::string currentText;
+  GetText( currentText );
+  DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::DoRelayout [%p] mImpl->mAutoScrollDirectionRTL[%s] [%s]\n", this, (mImpl->mAutoScrollDirectionRTL)?"true":"false",  currentText.c_str() );
+#endif
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
+  return viewUpdated;
+}
 
-      mImpl->mEventData->mUpdateCursorPosition = true;
+void Controller::CalculateVerticalOffset( const Size& controlSize )
+{
+  Size layoutSize = mImpl->mVisualModel->GetLayoutSize();
 
-      mImpl->RequestRelayout(); // Cursor, Handles, Selection Highlight, Popup
-      break;
-    }
-    case Toolkit::TextSelectionPopup::PASTE:
-    {
-      mImpl->RequestGetTextFromClipboard(); // Request clipboard service to retrieve an item
-      break;
-    }
-    case Toolkit::TextSelectionPopup::SELECT:
-    {
-      const Vector2& currentCursorPosition = mImpl->mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
+  if( fabsf( layoutSize.height ) < Math::MACHINE_EPSILON_1000 )
+  {
+    // Get the line height of the default font.
+    layoutSize.height = mImpl->GetDefaultFontLineHeight();
+  }
 
-      if( mImpl->mEventData->mSelectionEnabled )
-      {
-        // Creates a SELECT event.
-        SelectEvent( currentCursorPosition.x, currentCursorPosition.y, false );
-      }
-      break;
-    }
-    case Toolkit::TextSelectionPopup::SELECT_ALL:
+  switch( mImpl->mLayoutEngine.GetVerticalAlignment() )
+  {
+    case LayoutEngine::VERTICAL_ALIGN_TOP:
     {
-      // Creates a SELECT_ALL event
-      SelectEvent( 0.f, 0.f, true );
+      mImpl->mScrollPosition.y = 0.f;
       break;
     }
-    case Toolkit::TextSelectionPopup::CLIPBOARD:
+    case LayoutEngine::VERTICAL_ALIGN_CENTER:
     {
-      mImpl->ShowClipboard();
+      mImpl->mScrollPosition.y = floorf( 0.5f * ( controlSize.height - layoutSize.height ) ); // try to avoid pixel alignment.
       break;
     }
-    case Toolkit::TextSelectionPopup::NONE:
+    case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
     {
-      // Nothing to do.
+      mImpl->mScrollPosition.y = controlSize.height - layoutSize.height;
       break;
     }
   }
 }
 
-ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
+// private : Events.
+
+void Controller::ProcessModifyEvents()
 {
-  // Whether the text needs to be relaid-out.
-  bool requestRelayout = false;
+  Vector<ModifyEvent>& events = mImpl->mModifyEvents;
 
-  // Whether to retrieve the text and cursor position to be sent to the IMF manager.
-  bool retrieveText = false;
-  bool retrieveCursor = false;
+  if( 0u == events.Count() )
+  {
+    // Nothing to do.
+    return;
+  }
 
-  switch( imfEvent.eventName )
+  for( Vector<ModifyEvent>::ConstIterator it = events.Begin(),
+         endIt = events.End();
+       it != endIt;
+       ++it )
   {
-    case ImfManager::COMMIT:
+    const ModifyEvent& event = *it;
+
+    if( ModifyEvent::TEXT_REPLACED == event.type )
     {
-      InsertText( imfEvent.predictiveString, Text::Controller::COMMIT );
-      requestRelayout = true;
-      retrieveCursor = true;
-      break;
+      // A (single) replace event should come first, otherwise we wasted time processing NOOP events
+      DALI_ASSERT_DEBUG( it == events.Begin() && "Unexpected TEXT_REPLACED event" );
+
+      TextReplacedEvent();
     }
-    case ImfManager::PREEDIT:
+    else if( ModifyEvent::TEXT_INSERTED == event.type )
     {
-      InsertText( imfEvent.predictiveString, Text::Controller::PRE_EDIT );
-      requestRelayout = true;
-      retrieveCursor = true;
-      break;
+      TextInsertedEvent();
     }
-    case ImfManager::DELETESURROUNDING:
+    else if( ModifyEvent::TEXT_DELETED == event.type )
     {
-      const bool textDeleted = RemoveText( imfEvent.cursorOffset,
-                                           imfEvent.numberOfChars,
-                                           DONT_UPDATE_INPUT_STYLE );
-
-      if( textDeleted )
+      // Placeholder-text cannot be deleted
+      if( !mImpl->IsShowingPlaceholderText() )
       {
-        if( ( 0u != mImpl->mLogicalModel->mText.Count() ) ||
-            !mImpl->IsPlaceholderAvailable() )
-        {
-          mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
-        }
-        else
-        {
-          ShowPlaceholderText();
-        }
-        mImpl->mEventData->mUpdateCursorPosition = true;
-        mImpl->mEventData->mScrollAfterDelete = true;
-
-        requestRelayout = true;
+        TextDeletedEvent();
       }
-      break;
-    }
-    case ImfManager::GETSURROUNDING:
-    {
-      retrieveText = true;
-      retrieveCursor = true;
-      break;
-    }
-    case ImfManager::VOID:
-    {
-      // do nothing
-      break;
     }
-  } // end switch
+  }
 
-  if( requestRelayout )
+  if( NULL != mImpl->mEventData )
   {
-    mImpl->mOperationsPending = ALL_OPERATIONS;
-    mImpl->RequestRelayout();
+    // When the text is being modified, delay cursor blinking
+    mImpl->mEventData->mDecorator->DelayCursorBlink();
   }
 
-  std::string text;
-  CharacterIndex cursorPosition = 0u;
-  Length numberOfWhiteSpaces = 0u;
+  // Discard temporary text
+  events.Clear();
+}
 
-  if( retrieveCursor )
-  {
-    numberOfWhiteSpaces = mImpl->GetNumberOfWhiteSpaces( 0u );
+void Controller::TextReplacedEvent()
+{
+  // The natural size needs to be re-calculated.
+  mImpl->mRecalculateNaturalSize = true;
 
-    cursorPosition = mImpl->GetLogicalCursorPosition();
+  // Apply modifications to the model
+  mImpl->mOperationsPending = ALL_OPERATIONS;
+}
 
-    if( cursorPosition < numberOfWhiteSpaces )
-    {
-      cursorPosition = 0u;
-    }
-    else
-    {
-      cursorPosition -= numberOfWhiteSpaces;
-    }
-  }
+void Controller::TextInsertedEvent()
+{
+  DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextInsertedEvent" );
 
-  if( retrieveText )
+  if( NULL == mImpl->mEventData )
   {
-    mImpl->GetText( numberOfWhiteSpaces, text );
+    return;
   }
 
-  ImfManager::ImfCallbackData callbackData( ( retrieveText || retrieveCursor ), cursorPosition, text, false );
+  // The natural size needs to be re-calculated.
+  mImpl->mRecalculateNaturalSize = true;
 
-  if( requestRelayout )
+  // Apply modifications to the model; TODO - Optimize this
+  mImpl->mOperationsPending = ALL_OPERATIONS;
+}
+
+void Controller::TextDeletedEvent()
+{
+  DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextDeletedEvent" );
+
+  if( NULL == mImpl->mEventData )
   {
-    // Do this last since it provides callbacks into application code
-    mImpl->mControlInterface.TextChanged();
+    return;
   }
 
-  return callbackData;
+  // The natural size needs to be re-calculated.
+  mImpl->mRecalculateNaturalSize = true;
+
+  // Apply modifications to the model; TODO - Optimize this
+  mImpl->mOperationsPending = ALL_OPERATIONS;
 }
 
-Controller::~Controller()
+void Controller::SelectEvent( float x, float y, bool selectAll )
 {
-  delete mImpl;
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SelectEvent\n" );
+
+  if( NULL != mImpl->mEventData )
+  {
+    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();
+  }
 }
 
 bool Controller::BackspaceKeyEvent()
@@ -2884,6 +2858,30 @@ bool Controller::BackspaceKeyEvent()
   return removed;
 }
 
+// private : Helpers.
+
+void Controller::ResetText()
+{
+  // Reset buffers.
+  mImpl->mLogicalModel->mText.Clear();
+
+  // We have cleared everything including the placeholder-text
+  mImpl->PlaceholderCleared();
+
+  mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
+  mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
+  mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = 0u;
+
+  // Clear any previous text.
+  mImpl->mTextUpdateInfo.mClearAll = true;
+
+  // The natural size needs to be re-calculated.
+  mImpl->mRecalculateNaturalSize = true;
+
+  // Apply modifications to the model
+  mImpl->mOperationsPending = ALL_OPERATIONS;
+}
+
 void Controller::ShowPlaceholderText()
 {
   if( mImpl->IsPlaceholderAvailable() )
@@ -2986,12 +2984,48 @@ void Controller::ClearStyleData()
   mImpl->mLogicalModel->ClearFontDescriptionRuns();
 }
 
+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::IsEditingState( mImpl->mEventData->mState )  )
+    {
+      mImpl->mEventData->mUpdateCursorPosition = true;
+    }
+  }
+}
+
+void Controller::ResetScrollPosition()
+{
+  if( NULL != mImpl->mEventData )
+  {
+    // Reset the scroll position.
+    mImpl->mScrollPosition = Vector2::ZERO;
+    mImpl->mEventData->mScrollAfterUpdatePosition = true;
+  }
+}
+
+// private : Private contructors & copy operator.
+
 Controller::Controller( ControlInterface& controlInterface )
 : mImpl( NULL )
 {
   mImpl = new Controller::Impl( controlInterface );
 }
 
+// The copy constructor and operator are left unimplemented.
+
+// protected : Destructor.
+
+Controller::~Controller()
+{
+  delete mImpl;
+}
+
 } // namespace Text
 
 } // namespace Toolkit
index ebad670..07e1665 100644 (file)
@@ -44,15 +44,6 @@ typedef IntrusivePtr<Controller> ControllerPtr;
 typedef Dali::Toolkit::Text::ControlInterface ControlInterface;
 
 /**
- * @brief Different placeholder-text can be shown when the control is active/inactive.
- */
-enum PlaceholderType
-{
-  PLACEHOLDER_TYPE_ACTIVE,
-  PLACEHOLDER_TYPE_INACTIVE,
-};
-
-/**
  * @brief A Text Controller is used by UI Controls which display text.
  *
  * It manipulates the Logical & Visual text models on behalf of the UI Controls.
@@ -65,7 +56,7 @@ enum PlaceholderType
  */
 class Controller : public RefObject, public Decorator::ControllerInterface, public TextSelectionPopupCallbackInterface
 {
-public:
+public: // Enumerated types.
 
   /**
    * @brief Text related operations to be done in the relayout process.
@@ -108,14 +99,28 @@ public:
     DONT_UPDATE_INPUT_STYLE
   };
 
+  /**
+   * @brief Used to specify what has been updated after the Relayout() method has been called.
+   */
   enum UpdateTextType
   {
-    NONE_UPDATED      = 0x0,
-    MODEL_UPDATED     = 0x1,
-    DECORATOR_UPDATED = 0x2
+    NONE_UPDATED      = 0x0, ///< Nothing has been updated.
+    MODEL_UPDATED     = 0x1, ///< The text's model has been updated.
+    DECORATOR_UPDATED = 0x2  ///< The decoration has been updated.
   };
 
   /**
+   * @brief Different placeholder-text can be shown when the control is active/inactive.
+   */
+  enum PlaceholderType
+  {
+    PLACEHOLDER_TYPE_ACTIVE,
+    PLACEHOLDER_TYPE_INACTIVE,
+  };
+
+public: // Constructor.
+
+  /**
    * @brief Create a new instance of a Controller.
    *
    * @param[in] controlInterface An interface used to request a text relayout.
@@ -123,6 +128,8 @@ public:
    */
   static ControllerPtr New( ControlInterface& controlInterface );
 
+public: // Configure the text controller.
+
   /**
    * @brief Called to enable text input.
    *
@@ -230,6 +237,69 @@ public:
   bool IsSmoothHandlePanEnabled() const;
 
   /**
+   * @brief Sets the maximum number of characters that can be inserted into the TextModel
+   *
+   * @param[in] maxCharacters maximum number of characters to be accepted
+   */
+  void SetMaximumNumberOfCharacters( Length maxCharacters );
+
+  /**
+   * @brief Sets the maximum number of characters that can be inserted into the TextModel
+   *
+   * @param[in] maxCharacters maximum number of characters to be accepted
+   */
+  int GetMaximumNumberOfCharacters();
+
+  /**
+   * @brief Called to enable/disable cursor blink.
+   *
+   * @note Only editable controls should calls this.
+   * @param[in] enabled Whether the cursor should blink or not.
+   */
+  void SetEnableCursorBlink( bool enable );
+
+  /**
+   * @brief Query whether cursor blink is enabled.
+   *
+   * @return Whether the cursor should blink or not.
+   */
+  bool GetEnableCursorBlink() const;
+
+  /**
+   * @brief Whether to enable the multi-line layout.
+   *
+   * @param[in] enable \e true enables the multi-line (by default)
+   */
+  void SetMultiLineEnabled( bool enable );
+
+  /**
+   * @return Whether the multi-line layout is enabled.
+   */
+  bool IsMultiLineEnabled() const;
+
+  /**
+   * @copydoc Dali::Toolkit::Text::LayoutEngine::SetHorizontalAlignment()
+   */
+  void SetHorizontalAlignment( LayoutEngine::HorizontalAlignment alignment );
+
+  /**
+   * @copydoc Dali::Toolkit::Text::LayoutEngine::GetHorizontalAlignment()
+   */
+  LayoutEngine::HorizontalAlignment GetHorizontalAlignment() const;
+
+  /**
+   * @copydoc Dali::Toolkit::Text::LayoutEngine::SetVerticalAlignment()
+   */
+  void SetVerticalAlignment( LayoutEngine::VerticalAlignment alignment );
+
+  /**
+   * @copydoc Dali::Toolkit::Text::LayoutEngine::GetVerticalAlignment()
+   */
+  LayoutEngine::VerticalAlignment GetVerticalAlignment() const;
+
+public: // Update.
+
+  /**
    * @brief Replaces any text previously set.
    *
    * @note This will be converted into UTF-32 when stored in the text model.
@@ -245,22 +315,6 @@ public:
   void GetText( std::string& text ) const;
 
   /**
-   * @brief Remove a given number of characters
-   *
-   * When predictve text is used the pre-edit text is removed and inserted again with the new characters.
-   * The UpdateInputStyleType @type parameter if set to DONT_UPDATE_INPUT_STYLE avoids to update the input
-   * style when pre-edit text is removed.
-   *
-   * @param[in] cursorOffset Start position from the current cursor position to start deleting characters.
-   * @param[in] numberOfCharacters The number of characters to delete from the cursorOffset.
-   * @param[in] type Whether to update the input style.
-   * @return True if the remove was successful.
-   */
-  bool RemoveText( int cursorOffset,
-                   int numberOfCharacters,
-                   UpdateInputStyleType type  );
-
-  /**
    * @brief Replaces any placeholder text previously set.
    *
    * @param[in] type Different placeholder-text can be shown when the control is active/inactive.
@@ -277,18 +331,12 @@ public:
   void GetPlaceholderText( PlaceholderType type, std::string& text ) const;
 
   /**
-   * @brief Sets the maximum number of characters that can be inserted into the TextModel
-   *
-   * @param[in] maxCharacters maximum number of characters to be accepted
+   * @ brief Update the text after a font change
+   * @param[in] newDefaultFont The new font to change to
    */
-  void SetMaximumNumberOfCharacters( Length maxCharacters );
+  void UpdateAfterFontChange( const std::string& newDefaultFont );
 
-  /**
-   * @brief Sets the maximum number of characters that can be inserted into the TextModel
-   *
-   * @param[in] maxCharacters maximum number of characters to be accepted
-   */
-  int GetMaximumNumberOfCharacters();
+public: // Default style & Input style
 
   /**
    * @brief Set the default font family.
@@ -376,12 +424,6 @@ public:
   float GetDefaultPointSize() const;
 
   /**
-   * @ brief Update the text after a font change
-   * @param[in] newDefaultFont The new font to change to
-   */
-  void UpdateAfterFontChange( const std::string& newDefaultFont );
-
-  /**
    * @brief Set the text color
    *
    * @param textColor The text color
@@ -734,20 +776,21 @@ public:
    */
   const std::string& GetInputOutlineProperties() const;
 
+public: // Queries & retrieves.
+
   /**
-   * @brief Called to enable/disable cursor blink.
+   * @brief Return the layout engine.
    *
-   * @note Only editable controls should calls this.
-   * @param[in] enabled Whether the cursor should blink or not.
+   * @return A reference to the layout engine.
    */
-  void SetEnableCursorBlink( bool enable );
+  LayoutEngine& GetLayoutEngine();
 
   /**
-   * @brief Query whether cursor blink is enabled.
+   * @brief Return a view of the text.
    *
-   * @return Whether the cursor should blink or not.
+   * @return A reference to the view.
    */
-  bool GetEnableCursorBlink() const;
+  View& GetView();
 
   /**
    * @brief Query the current scroll position; the UI control is responsible for moving actors to this position.
@@ -766,6 +809,8 @@ public:
    */
   float GetHeightForWidth( float width );
 
+public: // Relayout.
+
   /**
    * @brief Triggers a relayout which updates View (if necessary).
    *
@@ -776,10 +821,7 @@ public:
    */
   UpdateTextType Relayout( const Size& size );
 
-  /**
-   * @brief Process queued events which modify the model.
-   */
-  void ProcessModifyEvents();
+public: // Input style change signals.
 
   /**
    * @return Whether the queue of input style changed signals is empty.
@@ -794,162 +836,170 @@ public:
    */
   void ProcessInputStyleChangedSignals();
 
-  /**
-   * @brief Used to remove placeholder text.
-   */
-  void ResetText();
+public: // Text-input Event Queuing.
 
   /**
-   * @brief Used to reset the cursor position after setting a new text.
-   *
-   * @param[in] cursorIndex Where to place the cursor.
+   * @brief Called by editable UI controls when keyboard focus is gained.
    */
-  void ResetCursorPosition( CharacterIndex cursorIndex );
+  void KeyboardFocusGainEvent();
 
   /**
-   * @brief Used to reset the scroll position after setting a new text.
+   * @brief Called by editable UI controls when focus is lost.
    */
-  void ResetScrollPosition();
+  void KeyboardFocusLostEvent();
 
   /**
-   * @brief Used to process an event queued from SetText()
+   * @brief Called by editable UI controls when key events are received.
+   *
+   * @param[in] event The key event.
+   * @param[in] type Used to distinguish between regular key events and IMF events.
    */
-  void TextReplacedEvent();
+  bool KeyEvent( const Dali::KeyEvent& event );
 
   /**
-   * @brief Used to process an event queued from key events etc.
+   * @brief Called by editable UI controls when a tap gesture occurs.
+   * @param[in] tapCount The number of taps.
+   * @param[in] x The x position relative to the top-left of the parent control.
+   * @param[in] y The y position relative to the top-left of the parent control.
    */
-  void TextInsertedEvent();
+  void TapEvent( unsigned int tapCount, float x, float y );
 
   /**
-   * @brief Used to process an event queued from backspace key etc.
+   * @brief Called by editable UI controls when a pan gesture occurs.
+   *
+   * @param[in] state The state of the gesture.
+   * @param[in] displacement This distance panned since the last pan gesture.
    */
-  void TextDeletedEvent();
+  void PanEvent( Gesture::State state, const Vector2& displacement );
 
   /**
-   * @brief Lays-out the text.
-   *
-   * GetNaturalSize(), GetHeightForWidth() and Relayout() calls this method.
+   * @brief Called by editable UI controls when a long press gesture occurs.
    *
-   * @param[in] size A the size of a bounding box to layout text within.
-   * @param[in] operations The layout operations which need to be done.
-   * @param[out] layoutSize The size of the laid-out text.
+   * @param[in] state The state of the gesture.
+   * @param[in] x The x position relative to the top-left of the parent control.
+   * @param[in] y The y position relative to the top-left of the parent control.
    */
-  bool DoRelayout( const Size& size,
-                   OperationsMask operations,
-                   Size& layoutSize );
+  void LongPressEvent( Gesture::State state, float x, float y );
 
   /**
-   * @brief Whether to enable the multi-line layout.
+   * @brief Event received from IMF manager
    *
-   * @param[in] enable \e true enables the multi-line (by default)
+   * @param[in] imfManager The IMF manager.
+   * @param[in] imfEvent The event received.
+   * @return A data struture indicating if update is needed, cursor position and current text.
    */
-  void SetMultiLineEnabled( bool enable );
+  ImfManager::ImfCallbackData OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent );
 
   /**
-   * @return Whether the multi-line layout is enabled.
+   * @brief Event from Clipboard notifying an Item has been selected for pasting
    */
-  bool IsMultiLineEnabled() const;
+  void PasteClipboardItemEvent();
+
+protected: // Inherit from Text::Decorator::ControllerInterface.
 
   /**
-   * @copydoc Dali::Toolkit::Text::LayoutEngine::SetHorizontalAlignment()
+   * @copydoc Dali::Toolkit::Text::Decorator::ControllerInterface::GetTargetSize()
    */
-  void SetHorizontalAlignment( LayoutEngine::HorizontalAlignment alignment );
+  virtual void GetTargetSize( Vector2& targetSize );
 
   /**
-   * @copydoc Dali::Toolkit::Text::LayoutEngine::GetHorizontalAlignment()
+   * @copydoc Dali::Toolkit::Text::Decorator::ControllerInterface::AddDecoration()
    */
-  LayoutEngine::HorizontalAlignment GetHorizontalAlignment() const;
+  virtual void AddDecoration( Actor& actor, bool needsClipping );
 
   /**
-   * @copydoc Dali::Toolkit::Text::LayoutEngine::SetVerticalAlignment()
+   * @copydoc Dali::Toolkit::Text::Decorator::ControllerInterface::DecorationEvent()
    */
-  void SetVerticalAlignment( LayoutEngine::VerticalAlignment alignment );
+  virtual void DecorationEvent( HandleType handle, HandleState state, float x, float y );
+
+protected: // Inherit from TextSelectionPopup::TextPopupButtonCallbackInterface.
 
   /**
-   * @copydoc Dali::Toolkit::Text::LayoutEngine::GetVerticalAlignment()
+   * @copydoc Dali::Toolkit::TextSelectionPopup::TextPopupButtonCallbackInterface::TextPopupButtonTouched()
    */
-  LayoutEngine::VerticalAlignment GetVerticalAlignment() const;
+  virtual void TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button );
+
+private: // Update.
 
   /**
-   * @brief Calulates the vertical offset to align the text inside the bounding box.
+   * @brief Called by editable UI controls when key events are received.
    *
-   * @param[in] size The size of the bounding box.
+   * @param[in] text The text to insert.
+   * @param[in] type Used to distinguish between regular key events and IMF events.
    */
-  void CalculateVerticalOffset( const Size& size );
+  void InsertText( const std::string& text, InsertType type );
 
   /**
-   * @brief Return the layout engine.
-   *
-   * @return A reference to the layout engine.
+   * @brief Paste given string into Text model
+   * @param[in] stringToPaste this string will be inserted into the text model
    */
-  LayoutEngine& GetLayoutEngine();
+  void PasteText( const std::string& stringToPaste );
 
   /**
-   * @brief Return a view of the text.
+   * @brief Remove a given number of characters
    *
-   * @return A reference to the view.
+   * When predictve text is used the pre-edit text is removed and inserted again with the new characters.
+   * The UpdateInputStyleType @type parameter if set to DONT_UPDATE_INPUT_STYLE avoids to update the input
+   * style when pre-edit text is removed.
+   *
+   * @param[in] cursorOffset Start position from the current cursor position to start deleting characters.
+   * @param[in] numberOfCharacters The number of characters to delete from the cursorOffset.
+   * @param[in] type Whether to update the input style.
+   * @return True if the remove was successful.
    */
-  View& GetView();
-
-  // Text-input Event Queuing
+  bool RemoveText( int cursorOffset,
+                   int numberOfCharacters,
+                   UpdateInputStyleType type  );
 
   /**
-   * @brief Called by editable UI controls when keyboard focus is gained.
+   * @brief Checks if text is selected and if so removes it.
+   * @return true if text was removed
    */
-  void KeyboardFocusGainEvent();
+  bool RemoveSelectedText();
 
-  /**
-   * @brief Called by editable UI controls when focus is lost.
-   */
-  void KeyboardFocusLostEvent();
+private: // Relayout.
 
   /**
-   * @brief Called by editable UI controls when key events are received.
+   * @brief Lays-out the text.
    *
-   * @param[in] event The key event.
-   * @param[in] type Used to distinguish between regular key events and IMF events.
+   * GetNaturalSize(), GetHeightForWidth() and Relayout() calls this method.
+   *
+   * @param[in] size A the size of a bounding box to layout text within.
+   * @param[in] operations The layout operations which need to be done.
+   * @param[out] layoutSize The size of the laid-out text.
    */
-  bool KeyEvent( const Dali::KeyEvent& event );
+  bool DoRelayout( const Size& size,
+                   OperationsMask operations,
+                   Size& layoutSize );
 
   /**
-   * @brief Called by editable UI controls when key events are received.
+   * @brief Calulates the vertical offset to align the text inside the bounding box.
    *
-   * @param[in] text The text to insert.
-   * @param[in] type Used to distinguish between regular key events and IMF events.
+   * @param[in] size The size of the bounding box.
    */
-  void InsertText( const std::string& text, InsertType type );
+  void CalculateVerticalOffset( const Size& size );
+
+private: // Events.
 
   /**
-   * @brief Checks if text is selected and if so removes it.
-   * @return true if text was removed
+   * @brief Process queued events which modify the model.
    */
-  bool RemoveSelectedText();
+  void ProcessModifyEvents();
 
   /**
-   * @brief Called by editable UI controls when a tap gesture occurs.
-   * @param[in] tapCount The number of taps.
-   * @param[in] x The x position relative to the top-left of the parent control.
-   * @param[in] y The y position relative to the top-left of the parent control.
+   * @brief Used to process an event queued from SetText()
    */
-  void TapEvent( unsigned int tapCount, float x, float y );
+  void TextReplacedEvent();
 
   /**
-   * @brief Called by editable UI controls when a pan gesture occurs.
-   *
-   * @param[in] state The state of the gesture.
-   * @param[in] displacement This distance panned since the last pan gesture.
+   * @brief Used to process an event queued from key events etc.
    */
-  void PanEvent( Gesture::State state, const Vector2& displacement );
+  void TextInsertedEvent();
 
   /**
-   * @brief Called by editable UI controls when a long press gesture occurs.
-   *
-   * @param[in] state The state of the gesture.
-   * @param[in] x The x position relative to the top-left of the parent control.
-   * @param[in] y The y position relative to the top-left of the parent control.
+   * @brief Used to process an event queued from backspace key etc.
    */
-  void LongPressEvent( Gesture::State state, float x, float y );
+  void TextDeletedEvent();
 
   /**
    * @brief Creates a selection event.
@@ -963,75 +1013,47 @@ public:
   void SelectEvent( float x, float y, bool selectAll );
 
   /**
-   * @brief Event received from IMF manager
+   * @brief Helper to KeyEvent() to handle the backspace case.
    *
-   * @param[in] imfManager The IMF manager.
-   * @param[in] imfEvent The event received.
-   * @return A data struture indicating if update is needed, cursor position and current text.
-   */
-  ImfManager::ImfCallbackData OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent );
-
-  /**
-   * @brief Paste given string into Text model
-   * @param[in] stringToPaste this string will be inserted into the text model
-   */
-  void PasteText( const std::string& stringToPaste );
-
-  /**
-   * @brief Event from Clipboard notifying an Item has been selected for pasting
+   * @return True if a character was deleted.
    */
-  void PasteClipboardItemEvent();
+  bool BackspaceKeyEvent();
 
-  /**
-   * @copydoc Dali::Toolkit::Text::Decorator::ControllerInterface::GetTargetSize()
-   */
-  virtual void GetTargetSize( Vector2& targetSize );
+private: // Helpers.
 
   /**
-   * @copydoc Dali::Toolkit::Text::Decorator::ControllerInterface::AddDecoration()
+   * @brief Used to remove the text included the placeholder text.
    */
-  virtual void AddDecoration( Actor& actor, bool needsClipping );
+  void ResetText();
 
   /**
-   * @copydoc Dali::Toolkit::Text::Decorator::ControllerInterface::DecorationEvent()
+   * @brief Helper to show the place holder text..
    */
-  virtual void DecorationEvent( HandleType handle, HandleState state, float x, float y );
+  void ShowPlaceholderText();
 
   /**
-   * @copydoc Dali::Toolkit::TextSelectionPopup::TextPopupButtonCallbackInterface::TextPopupButtonTouched()
+   * @brief Helper to clear font-specific data (only).
    */
-  virtual void TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button );
-
-protected:
+  void ClearFontData();
 
   /**
-   * @brief A reference counted object may only be deleted by calling Unreference().
+   * @brief Helper to clear text's style data.
    */
-  virtual ~Controller();
-
-private:
+  void ClearStyleData();
 
   /**
-   * @brief Helper to KeyEvent() to handle the backspace case.
+   * @brief Used to reset the cursor position after setting a new text.
    *
-   * @return True if a character was deleted.
-   */
-  bool BackspaceKeyEvent();
-
-  /**
-   * @brief Helper to clear font-specific data.
+   * @param[in] cursorIndex Where to place the cursor.
    */
-  void ShowPlaceholderText();
+  void ResetCursorPosition( CharacterIndex cursorIndex );
 
   /**
-   * @brief Helper to clear font-specific data (only).
+   * @brief Used to reset the scroll position after setting a new text.
    */
-  void ClearFontData();
+  void ResetScrollPosition();
 
-  /**
-   * @brief Helper to clear text's style data.
-   */
-  void ClearStyleData();
+private: // Private contructors & copy operator.
 
   /**
    * @brief Private constructor.
@@ -1044,6 +1066,13 @@ private:
   // Undefined
   Controller& operator=( const Controller& handle );
 
+protected: // Destructor.
+
+  /**
+   * @brief A reference counted object may only be deleted by calling Unreference().
+   */
+  virtual ~Controller();
+
 private:
 
   struct Impl;