+// protected : Inherit from TextSelectionPopup::TextPopupButtonCallbackInterface.
+
+void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button )
+{
+ if( NULL == mImpl->mEventData )
+ {
+ return;
+ }
+
+ switch( button )
+ {
+ case Toolkit::TextSelectionPopup::CUT:
+ {
+ mImpl->SendSelectionToClipboard( true ); // Synchronous call to modify text
+ mImpl->mOperationsPending = ALL_OPERATIONS;
+
+ if( ( 0u != mImpl->mModel->mLogicalModel->mText.Count() ) ||
+ !mImpl->IsPlaceholderAvailable() )
+ {
+ mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
+ }
+ else
+ {
+ ShowPlaceholderText();
+ }
+
+ mImpl->mEventData->mUpdateCursorPosition = true;
+ mImpl->mEventData->mScrollAfterDelete = true;
+
+ mImpl->RequestRelayout();
+
+ if( NULL != mImpl->mEditableControlInterface )
+ {
+ mImpl->mEditableControlInterface->TextChanged();
+ }
+ break;
+ }
+ case Toolkit::TextSelectionPopup::COPY:
+ {
+ mImpl->SendSelectionToClipboard( false ); // Text not modified
+
+ mImpl->mEventData->mUpdateCursorPosition = true;
+
+ 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( mImpl->mEventData->mSelectionEnabled )
+ {
+ // Creates a SELECT event.
+ SelectEvent( currentCursorPosition.x, currentCursorPosition.y, false );
+ }
+ break;
+ }
+ case Toolkit::TextSelectionPopup::SELECT_ALL:
+ {
+ // Creates a SELECT_ALL event
+ SelectEvent( 0.f, 0.f, true );
+ break;
+ }
+ case Toolkit::TextSelectionPopup::CLIPBOARD:
+ {
+ mImpl->ShowClipboard();
+ break;
+ }
+ case Toolkit::TextSelectionPopup::NONE:
+ {
+ // Nothing to do.
+ break;
+ }
+ }
+}
+
+// private : Update.
+
+void Controller::InsertText( const std::string& text, Controller::InsertType type )
+{
+ bool removedPrevious( false );
+ bool maxLengthReached( false );
+
+ DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
+
+ if( NULL == mImpl->mEventData )
+ {
+ return;
+ }
+
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n",
+ this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"),
+ mImpl->mEventData->mPrimaryCursorPosition, mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
+
+ // TODO: At the moment the underline runs are only for pre-edit.
+ mImpl->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 ) )
+ {
+ removedPrevious = RemoveText( -static_cast<int>( mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition ),
+ mImpl->mEventData->mPreEditLength,
+ DONT_UPDATE_INPUT_STYLE );
+
+ mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
+ mImpl->mEventData->mPreEditLength = 0u;
+ }
+ else
+ {
+ // Remove the previous Selection.
+ removedPrevious = RemoveSelectedText();
+ }
+
+ Vector<Character> utf32Characters;
+ Length characterCount = 0u;
+
+ if( !text.empty() )
+ {
+ // Convert text into UTF-32
+ utf32Characters.Resize( text.size() );
+
+ // This is a bit horrible but std::string returns a (signed) char*
+ const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
+
+ // Transform a text array encoded in utf8 into an array encoded in utf32.
+ // It returns the actual number of characters.
+ characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
+ utf32Characters.Resize( characterCount );
+
+ DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
+ }
+
+ if( 0u != utf32Characters.Count() ) // Check if Utf8ToUtf32 conversion succeeded
+ {
+ // The placeholder text is no longer needed
+ if( mImpl->IsShowingPlaceholderText() )
+ {
+ ResetText();
+ }
+
+ mImpl->ChangeState( EventData::EDITING );
+
+ // Handle the IMF (predicitive text) state changes
+ if( COMMIT == type )
+ {
+ // IMF manager is no longer handling key-events
+ mImpl->ClearPreEditFlag();
+ }
+ else // PRE_EDIT
+ {
+ if( !mImpl->mEventData->mPreEditFlag )
+ {
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Entered PreEdit state\n" );
+
+ // Record the start of the pre-edit text
+ mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
+ }
+
+ mImpl->mEventData->mPreEditLength = utf32Characters.Count();
+ mImpl->mEventData->mPreEditFlag = true;
+
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
+ }
+
+ const Length numberOfCharactersInModel = mImpl->mModel->mLogicalModel->mText.Count();
+
+ // Restrict new text to fit within Maximum characters setting.
+ Length maxSizeOfNewText = std::min( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
+ maxLengthReached = ( characterCount > maxSizeOfNewText );
+
+ // The cursor position.
+ CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
+
+ // Update the text's style.
+
+ // Updates the text style runs by adding characters.
+ mImpl->mModel->mLogicalModel->UpdateTextStyleRuns( cursorIndex, maxSizeOfNewText );
+
+ // Get the character index from the cursor index.
+ const CharacterIndex styleIndex = ( cursorIndex > 0u ) ? cursorIndex - 1u : 0u;
+
+ // Retrieve the text's style for the given index.
+ InputStyle style;
+ mImpl->RetrieveDefaultInputStyle( style );
+ mImpl->mModel->mLogicalModel->RetrieveStyle( styleIndex, style );
+
+ // Whether to add a new text color run.
+ const bool addColorRun = ( style.textColor != mImpl->mEventData->mInputStyle.textColor );
+
+ // Whether to add a new font run.
+ const bool addFontNameRun = style.familyName != mImpl->mEventData->mInputStyle.familyName;
+ const bool addFontWeightRun = style.weight != mImpl->mEventData->mInputStyle.weight;
+ const bool addFontWidthRun = style.width != mImpl->mEventData->mInputStyle.width;
+ const bool addFontSlantRun = style.slant != mImpl->mEventData->mInputStyle.slant;
+ const bool addFontSizeRun = style.size != mImpl->mEventData->mInputStyle.size;
+
+ // Add style runs.
+ if( addColorRun )
+ {
+ const VectorBase::SizeType numberOfRuns = mImpl->mModel->mLogicalModel->mColorRuns.Count();
+ mImpl->mModel->mLogicalModel->mColorRuns.Resize( numberOfRuns + 1u );
+
+ ColorRun& colorRun = *( mImpl->mModel->mLogicalModel->mColorRuns.Begin() + numberOfRuns );
+ colorRun.color = mImpl->mEventData->mInputStyle.textColor;
+ colorRun.characterRun.characterIndex = cursorIndex;
+ colorRun.characterRun.numberOfCharacters = maxSizeOfNewText;
+ }
+
+ if( addFontNameRun ||
+ addFontWeightRun ||
+ addFontWidthRun ||
+ addFontSlantRun ||
+ addFontSizeRun )
+ {
+ const VectorBase::SizeType numberOfRuns = mImpl->mModel->mLogicalModel->mFontDescriptionRuns.Count();
+ mImpl->mModel->mLogicalModel->mFontDescriptionRuns.Resize( numberOfRuns + 1u );
+
+ FontDescriptionRun& fontDescriptionRun = *( mImpl->mModel->mLogicalModel->mFontDescriptionRuns.Begin() + numberOfRuns );
+
+ if( addFontNameRun )
+ {
+ fontDescriptionRun.familyLength = mImpl->mEventData->mInputStyle.familyName.size();
+ fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
+ memcpy( fontDescriptionRun.familyName, mImpl->mEventData->mInputStyle.familyName.c_str(), fontDescriptionRun.familyLength );
+ fontDescriptionRun.familyDefined = true;
+
+ // The memory allocated for the font family name is freed when the font description is removed from the logical model.
+ }
+
+ if( addFontWeightRun )
+ {
+ fontDescriptionRun.weight = mImpl->mEventData->mInputStyle.weight;
+ fontDescriptionRun.weightDefined = true;
+ }
+
+ if( addFontWidthRun )
+ {
+ fontDescriptionRun.width = mImpl->mEventData->mInputStyle.width;
+ fontDescriptionRun.widthDefined = true;
+ }
+
+ if( addFontSlantRun )
+ {
+ fontDescriptionRun.slant = mImpl->mEventData->mInputStyle.slant;
+ fontDescriptionRun.slantDefined = true;
+ }
+
+ if( addFontSizeRun )
+ {
+ fontDescriptionRun.size = static_cast<PointSize26Dot6>( mImpl->mEventData->mInputStyle.size * 64.f );
+ fontDescriptionRun.sizeDefined = true;
+ }
+
+ fontDescriptionRun.characterRun.characterIndex = cursorIndex;
+ fontDescriptionRun.characterRun.numberOfCharacters = maxSizeOfNewText;
+ }
+
+ // Insert at current cursor position.
+ Vector<Character>& modifyText = mImpl->mModel->mLogicalModel->mText;
+
+ if( cursorIndex < numberOfCharactersInModel )
+ {
+ modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
+ }
+ else
+ {
+ modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
+ }
+
+ // Mark the first paragraph to be updated.
+ mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
+ mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd += maxSizeOfNewText;
+
+ // Update the cursor index.
+ cursorIndex += maxSizeOfNewText;
+
+ 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() )
+ {
+ // Show place-holder if empty after removing the pre-edit text
+ ShowPlaceholderText();
+ mImpl->mEventData->mUpdateCursorPosition = true;
+ mImpl->ClearPreEditFlag();
+ }
+ else if( removedPrevious ||
+ ( 0 != utf32Characters.Count() ) )
+ {
+ // Queue an inserted event
+ mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
+
+ mImpl->mEventData->mUpdateCursorPosition = true;
+ if( numberOfCharacters < currentNumberOfCharacters )
+ {
+ mImpl->mEventData->mScrollAfterDelete = true;
+ }
+ else
+ {
+ mImpl->mEventData->mScrollAfterUpdatePosition = true;
+ }
+ }
+
+ if( maxLengthReached )
+ {
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mModel->mLogicalModel->mText.Count() );
+
+ mImpl->ResetImfManager();
+
+ if( NULL != mImpl->mEditableControlInterface )
+ {
+ // Do this last since it provides callbacks into application code
+ mImpl->mEditableControlInterface->MaxLengthReached();
+ }
+ }
+}
+