Refactor TextLabel to use text visual
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller.cpp
index 831fd9c..8b0ee5b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 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.
 #include <dali/public-api/adaptor-framework/key.h>
 #include <dali/integration-api/debug.h>
 #include <dali/devel-api/adaptor-framework/clipboard-event-notifier.h>
+#include <dali/devel-api/text-abstraction/font-client.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/bidirectional-support.h>
 #include <dali-toolkit/internal/text/character-set-conversion.h>
 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
 #include <dali-toolkit/internal/text/markup-processor.h>
+#include <dali-toolkit/internal/text/multi-language-support.h>
 #include <dali-toolkit/internal/text/text-controller-impl.h>
 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
+#include <dali-toolkit/internal/text/text-font-style.h>
 
 namespace
 {
@@ -44,6 +47,14 @@ const float MAX_FLOAT = std::numeric_limits<float>::max();
 
 const std::string EMPTY_STRING("");
 
+const char * const PLACEHOLDER_TEXT = "placeholderText";
+const char * const PLACEHOLDER_TEXT_FOCUSED = "placeholderTextFocused";
+const char * const PLACEHOLDER_COLOR = "placeholderColor";
+const char * const PLACEHOLDER_FONT_FAMILY = "placeholderFontFamily";
+const char * const PLACEHOLDER_FONT_STYLE = "placeholderFontStyle";
+const char * const PLACEHOLDER_POINT_SIZE = "placeholderPointSize";
+const char * const PLACEHOLDER_PIXEL_SIZE = "placeholderPixelSize";
+
 float ConvertToEven( float value )
 {
   int intValue(static_cast<int>( value ));
@@ -381,6 +392,34 @@ Layout::VerticalAlignment Controller::GetVerticalAlignment() const
   return mImpl->mModel->mVerticalAlignment;
 }
 
+void Controller::SetLineWrapMode( Layout::LineWrap::Mode lineWrapMode )
+{
+  if( lineWrapMode != mImpl->mModel->mLineWrapMode )
+  {
+    // Set the text wrap mode.
+    mImpl->mModel->mLineWrapMode = lineWrapMode;
+
+
+    // Update Text layout for applying wrap mode
+    mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
+                                                             ALIGN                     |
+                                                             LAYOUT                    |
+                                                             UPDATE_LAYOUT_SIZE        |
+                                                             REORDER                   );
+    mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
+    mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
+    mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count();
+
+    // Request relayout
+    mImpl->RequestRelayout();
+  }
+}
+
+Layout::LineWrap::Mode Controller::GetLineWrapMode() const
+{
+  return mImpl->mModel->mLineWrapMode;
+}
+
 void Controller::SetTextElideEnabled( bool enabled )
 {
   mImpl->mModel->mElideEnabled = enabled;
@@ -391,6 +430,16 @@ bool Controller::IsTextElideEnabled() const
   return mImpl->mModel->mElideEnabled;
 }
 
+void Controller::SetSelectionEnabled( bool enabled )
+{
+  mImpl->mEventData->mSelectionEnabled = enabled;
+}
+
+bool Controller::IsSelectionEnabled() const
+{
+  return mImpl->mEventData->mSelectionEnabled;
+}
+
 // public : Update
 
 void Controller::SetText( const std::string& text )
@@ -592,6 +641,33 @@ const std::string& Controller::GetDefaultFontFamily() const
   return EMPTY_STRING;
 }
 
+void Controller::SetPlaceholderFontFamily( const std::string& placeholderTextFontFamily )
+{
+  if( NULL != mImpl->mEventData )
+  {
+    if( NULL == mImpl->mEventData->mPlaceholderFont )
+    {
+      mImpl->mEventData->mPlaceholderFont = new FontDefaults();
+    }
+
+    mImpl->mEventData->mPlaceholderFont->mFontDescription.family = placeholderTextFontFamily;
+    DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetPlaceholderFontFamily %s\n", placeholderTextFontFamily.c_str());
+    mImpl->mEventData->mPlaceholderFont->familyDefined = !placeholderTextFontFamily.empty();
+
+    mImpl->RequestRelayout();
+  }
+}
+
+const std::string& Controller::GetPlaceholderFontFamily() const
+{
+  if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
+  {
+    return mImpl->mEventData->mPlaceholderFont->mFontDescription.family;
+  }
+
+  return EMPTY_STRING;
+}
+
 void Controller::SetDefaultFontWeight( FontWeight weight )
 {
   if( NULL == mImpl->mFontDefaults )
@@ -623,6 +699,41 @@ FontWeight Controller::GetDefaultFontWeight() const
   return TextAbstraction::FontWeight::NORMAL;
 }
 
+void Controller::SetPlaceholderTextFontWeight( FontWeight weight )
+{
+  if( NULL != mImpl->mEventData )
+  {
+    if( NULL == mImpl->mEventData->mPlaceholderFont )
+    {
+      mImpl->mEventData->mPlaceholderFont = new FontDefaults();
+    }
+
+    mImpl->mEventData->mPlaceholderFont->mFontDescription.weight = weight;
+    mImpl->mEventData->mPlaceholderFont->weightDefined = true;
+
+    mImpl->RequestRelayout();
+  }
+}
+
+bool Controller::IsPlaceholderTextFontWeightDefined() const
+{
+  if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
+  {
+    return mImpl->mEventData->mPlaceholderFont->weightDefined;
+  }
+  return false;
+}
+
+FontWeight Controller::GetPlaceholderTextFontWeight() const
+{
+  if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
+  {
+    return mImpl->mEventData->mPlaceholderFont->mFontDescription.weight;
+  }
+
+  return TextAbstraction::FontWeight::NORMAL;
+}
+
 void Controller::SetDefaultFontWidth( FontWidth width )
 {
   if( NULL == mImpl->mFontDefaults )
@@ -654,6 +765,41 @@ FontWidth Controller::GetDefaultFontWidth() const
   return TextAbstraction::FontWidth::NORMAL;
 }
 
+void Controller::SetPlaceholderTextFontWidth( FontWidth width )
+{
+  if( NULL != mImpl->mEventData )
+  {
+    if( NULL == mImpl->mEventData->mPlaceholderFont )
+    {
+      mImpl->mEventData->mPlaceholderFont = new FontDefaults();
+    }
+
+    mImpl->mEventData->mPlaceholderFont->mFontDescription.width = width;
+    mImpl->mEventData->mPlaceholderFont->widthDefined = true;
+
+    mImpl->RequestRelayout();
+  }
+}
+
+bool Controller::IsPlaceholderTextFontWidthDefined() const
+{
+  if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
+  {
+    return mImpl->mEventData->mPlaceholderFont->widthDefined;
+  }
+  return false;
+}
+
+FontWidth Controller::GetPlaceholderTextFontWidth() const
+{
+  if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
+  {
+    return mImpl->mEventData->mPlaceholderFont->mFontDescription.width;
+  }
+
+  return TextAbstraction::FontWidth::NORMAL;
+}
+
 void Controller::SetDefaultFontSlant( FontSlant slant )
 {
   if( NULL == mImpl->mFontDefaults )
@@ -685,15 +831,69 @@ FontSlant Controller::GetDefaultFontSlant() const
   return TextAbstraction::FontSlant::NORMAL;
 }
 
-void Controller::SetDefaultPointSize( float pointSize )
+void Controller::SetPlaceholderTextFontSlant( FontSlant slant )
+{
+  if( NULL != mImpl->mEventData )
+  {
+    if( NULL == mImpl->mEventData->mPlaceholderFont )
+    {
+      mImpl->mEventData->mPlaceholderFont = new FontDefaults();
+    }
+
+    mImpl->mEventData->mPlaceholderFont->mFontDescription.slant = slant;
+    mImpl->mEventData->mPlaceholderFont->slantDefined = true;
+
+    mImpl->RequestRelayout();
+  }
+}
+
+bool Controller::IsPlaceholderTextFontSlantDefined() const
+{
+  if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
+  {
+    return mImpl->mEventData->mPlaceholderFont->slantDefined;
+  }
+  return false;
+}
+
+FontSlant Controller::GetPlaceholderTextFontSlant() const
+{
+  if( ( NULL != mImpl->mEventData ) && ( NULL != mImpl->mEventData->mPlaceholderFont ) )
+  {
+    return mImpl->mEventData->mPlaceholderFont->mFontDescription.slant;
+  }
+
+  return TextAbstraction::FontSlant::NORMAL;
+}
+
+void Controller::SetDefaultFontSize( float fontSize, FontSizeType type )
 {
   if( NULL == mImpl->mFontDefaults )
   {
     mImpl->mFontDefaults = new FontDefaults();
   }
 
-  mImpl->mFontDefaults->mDefaultPointSize = pointSize;
-  mImpl->mFontDefaults->sizeDefined = true;
+  switch( type )
+  {
+    case POINT_SIZE:
+    {
+      mImpl->mFontDefaults->mDefaultPointSize = fontSize;
+      mImpl->mFontDefaults->sizeDefined = true;
+      break;
+    }
+    case PIXEL_SIZE:
+    {
+      // Point size = Pixel size * 72.f / DPI
+      unsigned int horizontalDpi = 0u;
+      unsigned int verticalDpi = 0u;
+      TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+      fontClient.GetDpi( horizontalDpi, verticalDpi );
+
+      mImpl->mFontDefaults->mDefaultPointSize = ( fontSize * 72.f ) / static_cast< float >( horizontalDpi );
+      mImpl->mFontDefaults->sizeDefined = true;
+      break;
+    }
+  }
 
   // Clear the font-specific data
   ClearFontData();
@@ -701,14 +901,117 @@ void Controller::SetDefaultPointSize( float pointSize )
   mImpl->RequestRelayout();
 }
 
-float Controller::GetDefaultPointSize() const
+float Controller::GetDefaultFontSize( FontSizeType type ) const
 {
+  float value = 0.0f;
   if( NULL != mImpl->mFontDefaults )
   {
-    return mImpl->mFontDefaults->mDefaultPointSize;
+    switch( type )
+    {
+      case POINT_SIZE:
+      {
+        value = mImpl->mFontDefaults->mDefaultPointSize;
+        break;
+      }
+      case PIXEL_SIZE:
+      {
+        // Pixel size = Point size * DPI / 72.f
+        unsigned int horizontalDpi = 0u;
+        unsigned int verticalDpi = 0u;
+        TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+        fontClient.GetDpi( horizontalDpi, verticalDpi );
+
+        value = mImpl->mFontDefaults->mDefaultPointSize * static_cast< float >( horizontalDpi ) / 72.f;
+        break;
+      }
+    }
+    return value;
   }
 
-  return 0.0f;
+  return value;
+}
+
+void Controller::SetPlaceholderTextFontSize( float fontSize, FontSizeType type )
+{
+  if( NULL != mImpl->mEventData )
+  {
+    if( NULL == mImpl->mEventData->mPlaceholderFont )
+    {
+      mImpl->mEventData->mPlaceholderFont = new FontDefaults();
+    }
+
+    switch( type )
+    {
+      case POINT_SIZE:
+      {
+        mImpl->mEventData->mPlaceholderFont->mDefaultPointSize = fontSize;
+        mImpl->mEventData->mPlaceholderFont->sizeDefined = true;
+        mImpl->mEventData->mIsPlaceholderPixelSize = false; // Font size flag
+        break;
+      }
+      case PIXEL_SIZE:
+      {
+        // Point size = Pixel size * 72.f / DPI
+        unsigned int horizontalDpi = 0u;
+        unsigned int verticalDpi = 0u;
+        TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+        fontClient.GetDpi( horizontalDpi, verticalDpi );
+
+        mImpl->mEventData->mPlaceholderFont->mDefaultPointSize = ( fontSize * 72.f ) / static_cast< float >( horizontalDpi );
+        mImpl->mEventData->mPlaceholderFont->sizeDefined = true;
+        mImpl->mEventData->mIsPlaceholderPixelSize = true; // Font size flag
+        break;
+      }
+    }
+
+    mImpl->RequestRelayout();
+  }
+}
+
+float Controller::GetPlaceholderTextFontSize( FontSizeType type ) const
+{
+  float value = 0.0f;
+  if( NULL != mImpl->mEventData )
+  {
+    switch( type )
+    {
+      case POINT_SIZE:
+      {
+        if( NULL != mImpl->mEventData->mPlaceholderFont )
+        {
+          value = mImpl->mEventData->mPlaceholderFont->mDefaultPointSize;
+        }
+        else
+        {
+          // If the placeholder text font size is not set, then return the default font size.
+          value = GetDefaultFontSize( POINT_SIZE );
+        }
+        break;
+      }
+      case PIXEL_SIZE:
+      {
+        if( NULL != mImpl->mEventData->mPlaceholderFont )
+        {
+          // Pixel size = Point size * DPI / 72.f
+          unsigned int horizontalDpi = 0u;
+          unsigned int verticalDpi = 0u;
+          TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+          fontClient.GetDpi( horizontalDpi, verticalDpi );
+
+          value = mImpl->mEventData->mPlaceholderFont->mDefaultPointSize * static_cast< float >( horizontalDpi ) / 72.f;
+        }
+        else
+        {
+          // If the placeholder text font size is not set, then return the default font size.
+          value = GetDefaultFontSize( PIXEL_SIZE );
+        }
+        break;
+      }
+    }
+    return value;
+  }
+
+  return value;
 }
 
 void Controller::SetDefaultColor( const Vector4& color )
@@ -1219,7 +1522,7 @@ float Controller::GetInputFontPointSize() const
   }
 
   // Return the default font's point size if there is no EventData.
-  return GetDefaultPointSize();
+  return GetDefaultFontSize( Text::Controller::POINT_SIZE );
 }
 
 void Controller::SetInputLineSpacing( float lineSpacing )
@@ -1313,6 +1616,93 @@ const std::string& Controller::GetInputOutlineProperties() const
   return GetDefaultOutlineProperties();
 }
 
+void Controller::SetInputModePassword( bool passwordInput )
+{
+  if( NULL != mImpl->mEventData )
+  {
+    mImpl->mEventData->mPasswordInput = passwordInput;
+  }
+}
+
+bool Controller::IsInputModePassword()
+{
+  if( NULL != mImpl->mEventData )
+  {
+    return mImpl->mEventData->mPasswordInput;
+  }
+  return false;
+}
+
+void Controller::SetNoTextDoubleTapAction( NoTextTap::Action action )
+{
+  if( NULL != mImpl->mEventData )
+  {
+    mImpl->mEventData->mDoubleTapAction = action;
+  }
+}
+
+Controller::NoTextTap::Action Controller::GetNoTextDoubleTapAction() const
+{
+  NoTextTap::Action action = NoTextTap::NO_ACTION;
+
+  if( NULL != mImpl->mEventData )
+  {
+    action = mImpl->mEventData->mDoubleTapAction;
+  }
+
+  return action;
+}
+
+void Controller::SetNoTextLongPressAction( NoTextTap::Action action )
+{
+  if( NULL != mImpl->mEventData )
+  {
+    mImpl->mEventData->mLongPressAction = action;
+  }
+}
+
+Controller::NoTextTap::Action Controller::GetNoTextLongPressAction() const
+{
+  NoTextTap::Action action = NoTextTap::NO_ACTION;
+
+  if( NULL != mImpl->mEventData )
+  {
+    action = mImpl->mEventData->mLongPressAction;
+  }
+
+  return action;
+}
+
+bool Controller::IsUnderlineSetByString()
+{
+  return mImpl->mUnderlineSetByString;
+}
+
+void Controller::UnderlineSetByString( bool setByString )
+{
+  mImpl->mUnderlineSetByString = setByString;
+}
+
+bool Controller::IsShadowSetByString()
+{
+  return mImpl->mShadowSetByString;
+}
+
+void Controller::ShadowSetByString( bool setByString )
+{
+  mImpl->mShadowSetByString = setByString;
+}
+
+bool Controller::IsFontStyleSetByString()
+{
+  return mImpl->mFontStyleSetByString;
+}
+
+void Controller::FontStyleSetByString( bool setByString )
+{
+  mImpl->mFontStyleSetByString = setByString;
+}
+
 // public : Queries & retrieves.
 
 Layout::Engine& Controller::GetLayoutEngine()
@@ -1353,14 +1743,14 @@ Vector3 Controller::GetNaturalSize()
     mImpl->UpdateModel( onlyOnceOperations );
 
     // Layout the text for the new width.
-    mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | LAYOUT );
+    mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | LAYOUT | REORDER );
 
     // Store the actual control's size to restore later.
     const Size actualControlSize = mImpl->mModel->mVisualModel->mControlSize;
 
     DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
                 static_cast<OperationsMask>( onlyOnceOperations |
-                                             LAYOUT ),
+                                             LAYOUT | REORDER ),
                 naturalSize.GetVectorXY() );
 
     // Do not do again the only once operations.
@@ -1406,7 +1796,9 @@ float Controller::GetHeightForWidth( float width )
   ProcessModifyEvents();
 
   Size layoutSize;
-  if( fabsf( width - mImpl->mModel->mVisualModel->mControlSize.width ) > Math::MACHINE_EPSILON_1000 )
+  if( fabsf( width - mImpl->mModel->mVisualModel->mControlSize.width ) > Math::MACHINE_EPSILON_1000 ||
+                                                         mImpl->mTextUpdateInfo.mFullRelayoutNeeded ||
+                                                         mImpl->mTextUpdateInfo.mClearAll            )
   {
     // Operations that can be done only once until the text changes.
     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
@@ -1464,6 +1856,13 @@ float Controller::GetHeightForWidth( float width )
   return layoutSize.height;
 }
 
+int Controller::GetLineCount( float width )
+{
+  GetHeightForWidth( width );
+  int numberofLines = mImpl->mModel->GetNumberOfLines();
+  return numberofLines;
+}
+
 const ModelInterface* const Controller::GetTextModel() const
 {
   return mImpl->mModel.Get();
@@ -1481,6 +1880,129 @@ float Controller::GetScrollAmountByUserInput()
   return scrollAmount;
 }
 
+bool Controller::GetTextScrollInfo( float& scrollPosition, float& controlHeight, float& layoutHeight )
+{
+  const Vector2& layout = mImpl->mModel->mVisualModel->GetLayoutSize();
+  bool isScrolled;
+
+  controlHeight = mImpl->mModel->mVisualModel->mControlSize.height;
+  layoutHeight = layout.height;
+  scrollPosition = mImpl->mModel->mScrollPosition.y;
+  isScrolled = !Equals( mImpl->mModel->mScrollPosition.y, mImpl->mModel->mScrollPositionLast.y, Math::MACHINE_EPSILON_1 );
+  return isScrolled;
+}
+
+void Controller::SetHiddenInputOption(const Property::Map& options )
+{
+  if( NULL == mImpl->mHiddenInput )
+  {
+    mImpl->mHiddenInput = new HiddenText( this );
+  }
+  mImpl->mHiddenInput->SetProperties(options);
+}
+
+void Controller::GetHiddenInputOption(Property::Map& options )
+{
+  if( NULL != mImpl->mHiddenInput )
+  {
+    mImpl->mHiddenInput->GetProperties(options);
+  }
+}
+
+void Controller::SetPlaceholderProperty( const Property::Map& map )
+{
+  const Property::Map::SizeType count = map.Count();
+
+  for( Property::Map::SizeType position = 0; position < count; ++position )
+  {
+    KeyValuePair keyValue = map.GetKeyValue( position );
+    Property::Key& key = keyValue.first;
+    Property::Value& value = keyValue.second;
+
+    if( key == PLACEHOLDER_TEXT )
+    {
+      std::string text = "";
+      value.Get( text );
+      SetPlaceholderText( Controller::PLACEHOLDER_TYPE_INACTIVE, text );
+    }
+    else if( key == PLACEHOLDER_TEXT_FOCUSED )
+    {
+      std::string text = "";
+      value.Get( text );
+      SetPlaceholderText( Controller::PLACEHOLDER_TYPE_ACTIVE, text );
+    }
+    else if( key == PLACEHOLDER_COLOR )
+    {
+      Vector4 textColor;
+      value.Get( textColor );
+      if( GetPlaceholderTextColor() != textColor )
+      {
+        SetPlaceholderTextColor( textColor );
+      }
+    }
+    else if( key == PLACEHOLDER_FONT_FAMILY )
+    {
+      std::string fontFamily = "";
+      value.Get( fontFamily );
+      SetPlaceholderFontFamily( fontFamily );
+    }
+    else if( key == PLACEHOLDER_FONT_STYLE )
+    {
+      SetFontStyleProperty( this, value, Text::FontStyle::PLACEHOLDER );
+    }
+    else if( key == PLACEHOLDER_POINT_SIZE )
+    {
+      float pointSize;
+      value.Get( pointSize );
+      if( !Equals( GetPlaceholderTextFontSize( Text::Controller::POINT_SIZE ), pointSize ) )
+      {
+        SetPlaceholderTextFontSize( pointSize, Text::Controller::POINT_SIZE );
+      }
+    }
+    else if( key == PLACEHOLDER_PIXEL_SIZE )
+    {
+      float pixelSize;
+      value.Get( pixelSize );
+      if( !Equals( GetPlaceholderTextFontSize( Text::Controller::PIXEL_SIZE ), pixelSize ) )
+      {
+        SetPlaceholderTextFontSize( pixelSize, Text::Controller::PIXEL_SIZE );
+      }
+    }
+  }
+}
+
+void Controller::GetPlaceholderProperty( Property::Map& map )
+{
+  if( NULL != mImpl->mEventData )
+  {
+    if( !mImpl->mEventData->mPlaceholderTextActive.empty() )
+    {
+      map[ PLACEHOLDER_TEXT_FOCUSED ] = mImpl->mEventData->mPlaceholderTextActive;
+    }
+    if( !mImpl->mEventData->mPlaceholderTextInactive.empty() )
+    {
+      map[ PLACEHOLDER_TEXT ] = mImpl->mEventData->mPlaceholderTextInactive;
+    }
+
+    map[ PLACEHOLDER_COLOR ] = mImpl->mEventData->mPlaceholderTextColor;
+    map[ PLACEHOLDER_FONT_FAMILY ] = GetPlaceholderFontFamily();
+
+    Property::Value fontStyleMapGet;
+    GetFontStyleProperty( this, fontStyleMapGet, Text::FontStyle::PLACEHOLDER );
+    map[ PLACEHOLDER_FONT_STYLE ] = fontStyleMapGet;
+
+    // Choose font size : POINT_SIZE or PIXEL_SIZE
+    if( !mImpl->mEventData->mIsPlaceholderPixelSize )
+    {
+      map[ PLACEHOLDER_POINT_SIZE ] = GetPlaceholderTextFontSize( Text::Controller::POINT_SIZE );
+    }
+    else
+    {
+      map[ PLACEHOLDER_PIXEL_SIZE ] = GetPlaceholderTextFontSize( Text::Controller::PIXEL_SIZE );
+    }
+  }
+}
+
 // public : Relayout.
 
 Controller::UpdateTextType Controller::Relayout( const Size& size )
@@ -1682,7 +2204,8 @@ bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
 {
   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
 
-  bool textChanged( false );
+  bool textChanged = false;
+  bool relayoutNeeded = false;
 
   if( ( NULL != mImpl->mEventData ) &&
       ( keyEvent.state == KeyEvent::Down ) )
@@ -1690,38 +2213,76 @@ bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
     int keyCode = keyEvent.keyCode;
     const std::string& keyString = keyEvent.keyPressed;
 
+    const bool isNullKey = ( 0 == keyCode ) && ( keyString.empty() );
+
     // Pre-process to separate modifying events from non-modifying input events.
-    if( Dali::DALI_KEY_ESCAPE == keyCode )
+    if( isNullKey )
+    {
+      // In some platforms arrive key events with no key code.
+      // Do nothing.
+      return false;
+    }
+    else if( Dali::DALI_KEY_ESCAPE == keyCode )
     {
       // Escape key is a special case which causes focus loss
       KeyboardFocusLostEvent();
+
+      // Will request for relayout.
+      relayoutNeeded = true;
     }
     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 ) )
     {
-      mImpl->mEventData->mCheckScrollAmount = true;
+      // If don't have any text, do nothing.
+      if( !mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters )
+      {
+        return false;
+      }
+
+      uint32_t cursorPosition = mImpl->mEventData->mPrimaryCursorPosition;
+      uint32_t numberOfCharacters = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
+      uint32_t cursorLine = mImpl->mModel->mVisualModel->GetLineOfCharacter( cursorPosition );
+      uint32_t numberOfLines = mImpl->mModel->GetNumberOfLines();
+
+      // Logic to determine whether this text control will lose focus or not.
+      if( ( Dali::DALI_KEY_CURSOR_LEFT == keyCode && 0 == cursorPosition ) ||
+          ( Dali::DALI_KEY_CURSOR_RIGHT == keyCode && numberOfCharacters == cursorPosition) ||
+          ( Dali::DALI_KEY_CURSOR_DOWN == keyCode && cursorLine == numberOfLines -1 ) ||
+          ( Dali::DALI_KEY_CURSOR_DOWN == keyCode && numberOfCharacters == cursorPosition && cursorLine -1 == numberOfLines -1 ) ||
+          ( Dali::DALI_KEY_CURSOR_UP == keyCode && cursorLine == 0 ) ||
+          ( Dali::DALI_KEY_CURSOR_UP == keyCode && numberOfCharacters == cursorPosition && cursorLine == 1 ) )
+      {
+        return false;
+      }
 
+      mImpl->mEventData->mCheckScrollAmount = true;
       Event event( Event::CURSOR_KEY_EVENT );
       event.p1.mInt = keyCode;
       mImpl->mEventData->mEventQueue.push_back( event );
+
+      // Will request for relayout.
+      relayoutNeeded = true;
     }
     else if( Dali::DALI_KEY_BACKSPACE == keyCode )
     {
       textChanged = BackspaceKeyEvent();
+
+      // Will request for relayout.
+      relayoutNeeded = true;
     }
-    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 ) ||
+    else if( IsKey( keyEvent, Dali::DALI_KEY_POWER ) ||
+             IsKey( keyEvent, Dali::DALI_KEY_MENU ) ||
              IsKey( keyEvent, Dali::DALI_KEY_HOME ) )
     {
+      // Power key/Menu/Home key behaviour does not allow edit mode to resume.
       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
+
+      // Will request for relayout.
+      relayoutNeeded = true;
+
+      // This branch avoids calling the InsertText() method of the 'else' branch which can delete selected text.
     }
     else if( Dali::DALI_KEY_SHIFT_LEFT == keyCode )
     {
@@ -1729,6 +2290,13 @@ bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
       // and a character is typed after the type of a upper case latin character.
 
       // Do nothing.
+      return false;
+    }
+    else if( ( Dali::DALI_KEY_VOLUME_UP == keyCode ) || ( Dali::DALI_KEY_VOLUME_DOWN == keyCode ) )
+    {
+      // This branch avoids calling the InsertText() method of the 'else' branch which can delete selected text.
+      // Do nothing.
+      return false;
     }
     else
     {
@@ -1739,19 +2307,31 @@ bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
 
       InsertText( keyString, COMMIT );
       textChanged = true;
+
+      // Will request for relayout.
+      relayoutNeeded = true;
     }
 
     if ( ( mImpl->mEventData->mState != EventData::INTERRUPTED ) &&
          ( mImpl->mEventData->mState != EventData::INACTIVE ) &&
-         ( Dali::DALI_KEY_SHIFT_LEFT != keyCode ) )
+         ( !isNullKey ) &&
+         ( Dali::DALI_KEY_SHIFT_LEFT != keyCode ) &&
+         ( Dali::DALI_KEY_VOLUME_UP != keyCode ) &&
+         ( Dali::DALI_KEY_VOLUME_DOWN != 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 );
+
+      // Will request for relayout.
+      relayoutNeeded = true;
     }
 
-    mImpl->RequestRelayout();
+    if( relayoutNeeded )
+    {
+      mImpl->RequestRelayout();
+    }
   }
 
   if( textChanged &&
@@ -1818,9 +2398,12 @@ void Controller::TapEvent( unsigned int tapCount, float x, float y )
       if( mImpl->mEventData->mSelectionEnabled &&
           mImpl->IsShowingRealText() )
       {
-        SelectEvent( x, y, false );
+        relayoutNeeded = true;
+        mImpl->mEventData->mIsLeftHandleSelected = true;
+        mImpl->mEventData->mIsRightHandleSelected = true;
       }
     }
+
     // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
     if( relayoutNeeded )
     {
@@ -1861,35 +2444,42 @@ void Controller::LongPressEvent( Gesture::State state, float x, float y  )
   if( ( state == Gesture::Started ) &&
       ( NULL != mImpl->mEventData ) )
   {
-    if( !mImpl->IsShowingRealText() )
+    // The 1st long-press on inactive text-field is treated as tap
+    if( EventData::INACTIVE == mImpl->mEventData->mState )
+    {
+      mImpl->ChangeState( EventData::EDITING );
+
+      Event event( Event::TAP_EVENT );
+      event.p1.mUint = 1;
+      event.p2.mFloat = x;
+      event.p3.mFloat = y;
+      mImpl->mEventData->mEventQueue.push_back( event );
+
+      mImpl->RequestRelayout();
+    }
+    else if( !mImpl->IsShowingRealText() )
     {
       Event event( Event::LONG_PRESS_EVENT );
       event.p1.mInt = state;
+      event.p2.mFloat = x;
+      event.p3.mFloat = y;
       mImpl->mEventData->mEventQueue.push_back( event );
       mImpl->RequestRelayout();
     }
-    else
+    else if( !mImpl->IsClipboardVisible() )
     {
-      // The 1st long-press on inactive text-field is treated as tap
-      if( EventData::INACTIVE == mImpl->mEventData->mState )
-      {
-        mImpl->ChangeState( EventData::EDITING );
+      // Reset the imf manager to commit the pre-edit before selecting the text.
+      mImpl->ResetImfManager();
 
-        Event event( Event::TAP_EVENT );
-        event.p1.mUint = 1;
-        event.p2.mFloat = x;
-        event.p3.mFloat = y;
-        mImpl->mEventData->mEventQueue.push_back( event );
-
-        mImpl->RequestRelayout();
-      }
-      else if( !mImpl->IsClipboardVisible() )
-      {
-        // Reset the imf manger to commit the pre-edit before selecting the text.
-        mImpl->ResetImfManager();
+      Event event( Event::LONG_PRESS_EVENT );
+      event.p1.mInt = state;
+      event.p2.mFloat = x;
+      event.p3.mFloat = y;
+      mImpl->mEventData->mEventQueue.push_back( event );
+      mImpl->RequestRelayout();
 
-        SelectEvent( x, y, false );
-      }
+      mImpl->mEventData->mIsLeftHandleSelected = true;
+      mImpl->mEventData->mIsRightHandleSelected = true;
     }
   }
 }
@@ -1949,6 +2539,13 @@ ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, cons
       retrieveCursor = true;
       break;
     }
+    case ImfManager::PRIVATECOMMAND:
+    {
+      // PRIVATECOMMAND event is just for getting the private command message
+      retrieveText = true;
+      retrieveCursor = true;
+      break;
+    }
     case ImfManager::VOID:
     {
       // do nothing
@@ -2167,12 +2764,22 @@ void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Butt
   }
 }
 
+void Controller::DisplayTimeExpired()
+{
+  mImpl->mEventData->mUpdateCursorPosition = true;
+  // Apply modifications to the model
+  mImpl->mOperationsPending = ALL_OPERATIONS;
+
+  mImpl->RequestRelayout();
+}
+
 // private : Update.
 
 void Controller::InsertText( const std::string& text, Controller::InsertType type )
 {
-  bool removedPrevious( false );
-  bool maxLengthReached( false );
+  bool removedPrevious = false;
+  bool removedSelected = false;
+  bool maxLengthReached = false;
 
   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
 
@@ -2188,9 +2795,6 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ
   // TODO: At the moment the underline runs are only for pre-edit.
   mImpl->mModel->mVisualModel->mUnderlineRuns.Clear();
 
-  // Keep the current number of characters.
-  const Length currentNumberOfCharacters = mImpl->IsShowingRealText() ? mImpl->mModel->mLogicalModel->mText.Count() : 0u;
-
   // Remove the previous IMF pre-edit.
   if( mImpl->mEventData->mPreEditFlag && ( 0u != mImpl->mEventData->mPreEditLength ) )
   {
@@ -2204,7 +2808,8 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ
   else
   {
     // Remove the previous Selection.
-    removedPrevious = RemoveSelectedText();
+    removedSelected = RemoveSelectedText();
+
   }
 
   Vector<Character> utf32Characters;
@@ -2365,8 +2970,18 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ
     }
 
     // Mark the first paragraph to be updated.
-    mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
-    mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd += maxSizeOfNewText;
+    if( Layout::Engine::SINGLE_LINE_BOX == mImpl->mLayoutEngine.GetLayout() )
+    {
+      mImpl->mTextUpdateInfo.mCharacterIndex = 0;
+      mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
+      mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = numberOfCharactersInModel + maxSizeOfNewText;
+      mImpl->mTextUpdateInfo.mClearAll = true;
+    }
+    else
+    {
+      mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
+      mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd += maxSizeOfNewText;
+    }
 
     // Update the cursor index.
     cursorIndex += maxSizeOfNewText;
@@ -2374,8 +2989,6 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ
     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mModel->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
   }
 
-  const Length numberOfCharacters = mImpl->IsShowingRealText() ? mImpl->mModel->mLogicalModel->mText.Count() : 0u;
-
   if( ( 0u == mImpl->mModel->mLogicalModel->mText.Count() ) &&
       mImpl->IsPlaceholderAvailable() )
   {
@@ -2385,13 +2998,14 @@ void Controller::InsertText( const std::string& text, Controller::InsertType typ
     mImpl->ClearPreEditFlag();
   }
   else if( removedPrevious ||
+           removedSelected ||
            ( 0 != utf32Characters.Count() ) )
   {
     // Queue an inserted event
     mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
 
     mImpl->mEventData->mUpdateCursorPosition = true;
-    if( numberOfCharacters < currentNumberOfCharacters )
+    if( removedSelected )
     {
       mImpl->mEventData->mScrollAfterDelete = true;
     }
@@ -2448,12 +3062,12 @@ bool Controller::RemoveText( int cursorOffset,
     Vector<Character>& currentText = mImpl->mModel->mLogicalModel->mText;
     CharacterIndex& oldCursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
 
-    CharacterIndex cursorIndex = oldCursorIndex;
+    CharacterIndex cursorIndex = 0;
 
     // Validate the cursor position & number of characters
-    if( static_cast< CharacterIndex >( std::abs( cursorOffset ) ) <= cursorIndex )
+    if( ( static_cast< int >( mImpl->mEventData->mPrimaryCursorPosition ) + cursorOffset ) >= 0 )
     {
-      cursorIndex = oldCursorIndex + cursorOffset;
+      cursorIndex = mImpl->mEventData->mPrimaryCursorPosition + cursorOffset;
     }
 
     if( ( cursorIndex + numberOfCharacters ) > currentText.Count() )
@@ -2465,8 +3079,18 @@ bool Controller::RemoveText( int cursorOffset,
         ( ( cursorIndex + numberOfCharacters ) <= mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters ) )
     {
       // Mark the paragraphs to be updated.
-      mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
-      mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove += numberOfCharacters;
+      if( Layout::Engine::SINGLE_LINE_BOX == mImpl->mLayoutEngine.GetLayout() )
+      {
+        mImpl->mTextUpdateInfo.mCharacterIndex = 0;
+        mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
+        mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters - numberOfCharacters;
+        mImpl->mTextUpdateInfo.mClearAll = true;
+      }
+      else
+      {
+        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.
 
@@ -2603,7 +3227,8 @@ bool Controller::DoRelayout( const Size& size,
                                          charactersToGlyphBuffer,
                                          glyphsPerCharacterBuffer,
                                          totalNumberOfGlyphs,
-                                         mImpl->mModel->mHorizontalAlignment );
+                                         mImpl->mModel->mHorizontalAlignment,
+                                         mImpl->mModel->mLineWrapMode );
 
     // Resize the vector of positions to have the same size than the vector of glyphs.
     Vector<Vector2>& glyphPositions = mImpl->mModel->mVisualModel->mGlyphPositions;
@@ -2857,6 +3482,8 @@ void Controller::SelectEvent( float x, float y, bool selectAll )
     }
 
     mImpl->mEventData->mCheckScrollAmount = true;
+    mImpl->mEventData->mIsLeftHandleSelected = true;
+    mImpl->mEventData->mIsRightHandleSelected = true;
     mImpl->RequestRelayout();
   }
 }
@@ -2950,7 +3577,7 @@ void Controller::ShowPlaceholderText()
     const char* text( NULL );
     size_t size( 0 );
 
-    // TODO - Switch placeholder text styles when changing state
+    // TODO - Switch Placeholder text when changing state
     if( ( EventData::INACTIVE != mImpl->mEventData->mState ) &&
         ( 0u != mImpl->mEventData->mPlaceholderTextActive.c_str() ) )
     {
@@ -3056,6 +3683,11 @@ void Controller::ResetScrollPosition()
   }
 }
 
+void Controller::SetControlInterface( ControlInterface* controlInterface )
+{
+  mImpl->mControlInterface = controlInterface;
+}
+
 // private : Private contructors & copy operator.
 
 Controller::Controller()