Merge "Add the alignment offset to the glyph lines." into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/text-controller.h>
20
21 // EXTERNAL INCLUDES
22 #include <limits>
23 #include <iostream>
24 #include <dali/public-api/adaptor-framework/key.h>
25 #include <dali/integration-api/debug.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/character-set-conversion.h>
30 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
31 #include <dali-toolkit/internal/text/multi-language-support.h>
32 #include <dali-toolkit/internal/text/script-run.h>
33 #include <dali-toolkit/internal/text/segmentation.h>
34 #include <dali-toolkit/internal/text/shaper.h>
35 #include <dali-toolkit/internal/text/text-controller-impl.h>
36 #include <dali-toolkit/internal/text/text-io.h>
37 #include <dali-toolkit/internal/text/text-view.h>
38
39 namespace
40 {
41
42 #if defined(DEBUG_ENABLED)
43   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_CONTROLS");
44 #endif
45
46 const float MAX_FLOAT = std::numeric_limits<float>::max();
47
48 const std::string EMPTY_STRING("");
49
50 } // namespace
51
52 namespace Dali
53 {
54
55 namespace Toolkit
56 {
57
58 namespace Text
59 {
60
61 ControllerPtr Controller::New( ControlInterface& controlInterface )
62 {
63   return ControllerPtr( new Controller( controlInterface ) );
64 }
65
66 void Controller::EnableTextInput( DecoratorPtr decorator )
67 {
68   if( !mImpl->mEventData )
69   {
70     mImpl->mEventData = new EventData( decorator );
71   }
72 }
73
74 void Controller::SetText( const std::string& text )
75 {
76   // Remove the previously set text
77   ResetText();
78
79   CharacterIndex lastCursorIndex = 0u;
80
81   if( !text.empty() )
82   {
83     //  Convert text into UTF-32
84     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
85     utf32Characters.Resize( text.size() );
86
87     // This is a bit horrible but std::string returns a (signed) char*
88     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
89
90     // Transform a text array encoded in utf8 into an array encoded in utf32.
91     // It returns the actual number of characters.
92     Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
93     utf32Characters.Resize( characterCount );
94
95     DALI_ASSERT_DEBUG( text.size() >= characterCount && "Invalid UTF32 conversion length" );
96     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText %p UTF8 size %d, UTF32 size %d\n", this, text.size(), mImpl->mLogicalModel->mText.Count() );
97
98     // To reset the cursor position
99     lastCursorIndex = characterCount;
100
101     // Update the rest of the model during size negotiation
102     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
103
104     // The natural size needs to be re-calculated.
105     mImpl->mRecalculateNaturalSize = true;
106
107     // Apply modifications to the model
108     mImpl->mOperationsPending = ALL_OPERATIONS;
109   }
110   else
111   {
112     ShowPlaceholderText();
113   }
114
115   // Resets the cursor position.
116   ResetCursorPosition( lastCursorIndex );
117
118   // Scrolls the text to make the cursor visible.
119   ResetScrollPosition();
120
121   mImpl->RequestRelayout();
122
123   if( mImpl->mEventData )
124   {
125     // Cancel previously queued events
126     mImpl->mEventData->mEventQueue.clear();
127   }
128
129   // Reset keyboard as text changed
130   mImpl->ResetImfManager();
131
132   // Do this last since it provides callbacks into application code
133   mImpl->mControlInterface.TextChanged();
134 }
135
136 void Controller::GetText( std::string& text ) const
137 {
138   if( ! mImpl->IsShowingPlaceholderText() )
139   {
140     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
141
142     if( 0u != utf32Characters.Count() )
143     {
144       Utf32ToUtf8( &utf32Characters[0], utf32Characters.Count(), text );
145     }
146   }
147   else
148   {
149     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::GetText %p empty (but showing placeholder)\n", this );
150   }
151 }
152
153 unsigned int Controller::GetLogicalCursorPosition() const
154 {
155   if( mImpl->mEventData )
156   {
157     return mImpl->mEventData->mPrimaryCursorPosition;
158   }
159
160   return 0u;
161 }
162
163 void Controller::SetPlaceholderText( PlaceholderType type, const std::string& text )
164 {
165   if( mImpl->mEventData )
166   {
167     if( PLACEHOLDER_TYPE_INACTIVE == type )
168     {
169       mImpl->mEventData->mPlaceholderTextInactive = text;
170     }
171     else
172     {
173       mImpl->mEventData->mPlaceholderTextActive = text;
174     }
175
176     // Update placeholder if there is no text
177     if( mImpl->IsShowingPlaceholderText() ||
178         0u == mImpl->mLogicalModel->mText.Count() )
179     {
180       ShowPlaceholderText();
181     }
182   }
183 }
184
185 void Controller::GetPlaceholderText( PlaceholderType type, std::string& text ) const
186 {
187   if( mImpl->mEventData )
188   {
189     if( PLACEHOLDER_TYPE_INACTIVE == type )
190     {
191       text = mImpl->mEventData->mPlaceholderTextInactive;
192     }
193     else
194     {
195       text = mImpl->mEventData->mPlaceholderTextActive;
196     }
197   }
198 }
199
200 void Controller::SetMaximumNumberOfCharacters( int maxCharacters )
201 {
202   if ( maxCharacters >= 0 )
203   {
204     mImpl->mMaximumNumberOfCharacters = maxCharacters;
205   }
206 }
207
208 int Controller::GetMaximumNumberOfCharacters()
209 {
210   return mImpl->mMaximumNumberOfCharacters;
211 }
212
213 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
214 {
215   if( !mImpl->mFontDefaults )
216   {
217     mImpl->mFontDefaults = new FontDefaults();
218   }
219
220   mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
221
222   // Clear the font-specific data
223   ClearFontData();
224
225   mImpl->mOperationsPending = ALL_OPERATIONS;
226   mImpl->mRecalculateNaturalSize = true;
227
228   mImpl->RequestRelayout();
229 }
230
231 const std::string& Controller::GetDefaultFontFamily() const
232 {
233   if( mImpl->mFontDefaults )
234   {
235     return mImpl->mFontDefaults->mDefaultFontFamily;
236   }
237
238   return EMPTY_STRING;
239 }
240
241 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
242 {
243   if( !mImpl->mFontDefaults )
244   {
245     mImpl->mFontDefaults = new FontDefaults();
246   }
247
248   mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
249
250   // Clear the font-specific data
251   ClearFontData();
252
253   mImpl->mOperationsPending = ALL_OPERATIONS;
254   mImpl->mRecalculateNaturalSize = true;
255
256   mImpl->RequestRelayout();
257 }
258
259 const std::string& Controller::GetDefaultFontStyle() const
260 {
261   if( mImpl->mFontDefaults )
262   {
263     return mImpl->mFontDefaults->mDefaultFontStyle;
264   }
265
266   return EMPTY_STRING;
267 }
268
269 void Controller::SetDefaultPointSize( float pointSize )
270 {
271   if( !mImpl->mFontDefaults )
272   {
273     mImpl->mFontDefaults = new FontDefaults();
274   }
275
276   mImpl->mFontDefaults->mDefaultPointSize = pointSize;
277
278   // Clear the font-specific data
279   ClearFontData();
280
281   mImpl->mOperationsPending = ALL_OPERATIONS;
282   mImpl->mRecalculateNaturalSize = true;
283
284   mImpl->RequestRelayout();
285 }
286
287 float Controller::GetDefaultPointSize() const
288 {
289   if( mImpl->mFontDefaults )
290   {
291     return mImpl->mFontDefaults->mDefaultPointSize;
292   }
293
294   return 0.0f;
295 }
296
297 void Controller::SetTextColor( const Vector4& textColor )
298 {
299   mImpl->mTextColor = textColor;
300
301   if( !mImpl->IsShowingPlaceholderText() )
302   {
303     mImpl->mVisualModel->SetTextColor( textColor );
304
305     mImpl->RequestRelayout();
306   }
307 }
308
309 const Vector4& Controller::GetTextColor() const
310 {
311   return mImpl->mTextColor;
312 }
313
314 bool Controller::RemoveText( int cursorOffset, int numberOfChars )
315 {
316   bool removed( false );
317
318   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfChars %d\n",
319                  this, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition, cursorOffset, numberOfChars );
320
321   if( ! mImpl->IsShowingPlaceholderText() )
322   {
323     // Delete at current cursor position
324     Vector<Character>& currentText = mImpl->mLogicalModel->mText;
325     CharacterIndex& oldCursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
326
327     CharacterIndex cursorIndex = oldCursorIndex;
328
329     // Validate the cursor position & number of characters
330     if( static_cast< CharacterIndex >( std::abs( cursorOffset ) ) <= cursorIndex )
331     {
332       cursorIndex = oldCursorIndex + cursorOffset;
333     }
334
335     if( (cursorIndex + numberOfChars) > currentText.Count() )
336     {
337       numberOfChars = currentText.Count() - cursorIndex;
338     }
339
340     if( cursorIndex >= 0 &&
341         (cursorIndex + numberOfChars) <= currentText.Count() )
342     {
343       Vector<Character>::Iterator first = currentText.Begin() + cursorIndex;
344       Vector<Character>::Iterator last  = first + numberOfChars;
345
346       currentText.Erase( first, last );
347
348       // Cursor position retreat
349       oldCursorIndex = cursorIndex;
350
351       DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", this, numberOfChars );
352       removed = true;
353     }
354   }
355
356   return removed;
357 }
358
359 void Controller::SetPlaceholderTextColor( const Vector4& textColor )
360 {
361   if( mImpl->mEventData )
362   {
363     mImpl->mEventData->mPlaceholderTextColor = textColor;
364   }
365
366   if( mImpl->IsShowingPlaceholderText() )
367   {
368     mImpl->mVisualModel->SetTextColor( textColor );
369     mImpl->RequestRelayout();
370   }
371 }
372
373 const Vector4& Controller::GetPlaceholderTextColor() const
374 {
375   if( mImpl->mEventData )
376   {
377     return mImpl->mEventData->mPlaceholderTextColor;
378   }
379
380   return Color::BLACK;
381 }
382
383 void Controller::SetShadowOffset( const Vector2& shadowOffset )
384 {
385   mImpl->mVisualModel->SetShadowOffset( shadowOffset );
386
387   mImpl->RequestRelayout();
388 }
389
390 const Vector2& Controller::GetShadowOffset() const
391 {
392   return mImpl->mVisualModel->GetShadowOffset();
393 }
394
395 void Controller::SetShadowColor( const Vector4& shadowColor )
396 {
397   mImpl->mVisualModel->SetShadowColor( shadowColor );
398
399   mImpl->RequestRelayout();
400 }
401
402 const Vector4& Controller::GetShadowColor() const
403 {
404   return mImpl->mVisualModel->GetShadowColor();
405 }
406
407 void Controller::SetUnderlineColor( const Vector4& color )
408 {
409   mImpl->mVisualModel->SetUnderlineColor( color );
410
411   mImpl->RequestRelayout();
412 }
413
414 const Vector4& Controller::GetUnderlineColor() const
415 {
416   return mImpl->mVisualModel->GetUnderlineColor();
417 }
418
419 void Controller::SetUnderlineEnabled( bool enabled )
420 {
421   mImpl->mVisualModel->SetUnderlineEnabled( enabled );
422
423   mImpl->RequestRelayout();
424 }
425
426 bool Controller::IsUnderlineEnabled() const
427 {
428   return mImpl->mVisualModel->IsUnderlineEnabled();
429 }
430
431 void Controller::SetUnderlineHeight( float height )
432 {
433   mImpl->mVisualModel->SetUnderlineHeight( height );
434
435   mImpl->RequestRelayout();
436 }
437
438 float Controller::GetUnderlineHeight() const
439 {
440   return mImpl->mVisualModel->GetUnderlineHeight();
441 }
442
443 void Controller::SetEnableCursorBlink( bool enable )
444 {
445   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "TextInput disabled" );
446
447   if( mImpl->mEventData )
448   {
449     mImpl->mEventData->mCursorBlinkEnabled = enable;
450
451     if( !enable &&
452         mImpl->mEventData->mDecorator )
453     {
454       mImpl->mEventData->mDecorator->StopCursorBlink();
455     }
456   }
457 }
458
459 bool Controller::GetEnableCursorBlink() const
460 {
461   if( mImpl->mEventData )
462   {
463     return mImpl->mEventData->mCursorBlinkEnabled;
464   }
465
466   return false;
467 }
468
469 const Vector2& Controller::GetScrollPosition() const
470 {
471   if( mImpl->mEventData )
472   {
473     return mImpl->mEventData->mScrollPosition;
474   }
475
476   return Vector2::ZERO;
477 }
478
479 const Vector2& Controller::GetAlignmentOffset() const
480 {
481   return mImpl->mAlignmentOffset;
482 }
483
484 Vector3 Controller::GetNaturalSize()
485 {
486   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetNaturalSize\n" );
487   Vector3 naturalSize;
488
489   // Make sure the model is up-to-date before layouting
490   ProcessModifyEvents();
491
492   if( mImpl->mRecalculateNaturalSize )
493   {
494     // Operations that can be done only once until the text changes.
495     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
496                                                                            GET_SCRIPTS       |
497                                                                            VALIDATE_FONTS    |
498                                                                            GET_LINE_BREAKS   |
499                                                                            GET_WORD_BREAKS   |
500                                                                            BIDI_INFO         |
501                                                                            SHAPE_TEXT        |
502                                                                            GET_GLYPH_METRICS );
503     // Make sure the model is up-to-date before layouting
504     mImpl->UpdateModel( onlyOnceOperations );
505
506     // Operations that need to be done if the size changes.
507     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
508                                                                         ALIGN  |
509                                                                         REORDER );
510
511     DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
512                 static_cast<OperationsMask>( onlyOnceOperations |
513                                              sizeOperations ),
514                 naturalSize.GetVectorXY() );
515
516     // Do not do again the only once operations.
517     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
518
519     // Do the size related operations again.
520     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
521
522     // Stores the natural size to avoid recalculate it again
523     // unless the text/style changes.
524     mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
525
526     mImpl->mRecalculateNaturalSize = false;
527
528     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize calculated %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
529   }
530   else
531   {
532     naturalSize = mImpl->mVisualModel->GetNaturalSize();
533
534     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize cached %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
535   }
536
537   return naturalSize;
538 }
539
540 float Controller::GetHeightForWidth( float width )
541 {
542   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetHeightForWidth %p width %f\n", this, width );
543   // Make sure the model is up-to-date before layouting
544   ProcessModifyEvents();
545
546   Size layoutSize;
547   if( width != mImpl->mControlSize.width )
548   {
549     // Operations that can be done only once until the text changes.
550     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
551                                                                            GET_SCRIPTS       |
552                                                                            VALIDATE_FONTS    |
553                                                                            GET_LINE_BREAKS   |
554                                                                            GET_WORD_BREAKS   |
555                                                                            BIDI_INFO         |
556                                                                            SHAPE_TEXT        |
557                                                                            GET_GLYPH_METRICS );
558     // Make sure the model is up-to-date before layouting
559     mImpl->UpdateModel( onlyOnceOperations );
560
561     // Operations that need to be done if the size changes.
562     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
563                                                                         ALIGN  |
564                                                                         REORDER );
565
566     DoRelayout( Size( width, MAX_FLOAT ),
567                 static_cast<OperationsMask>( onlyOnceOperations |
568                                              sizeOperations ),
569                 layoutSize );
570
571     // Do not do again the only once operations.
572     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
573
574     // Do the size related operations again.
575     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
576     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth calculated %f\n", layoutSize.height );
577   }
578   else
579   {
580     layoutSize = mImpl->mVisualModel->GetActualSize();
581     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetHeightForWidth cached %f\n", layoutSize.height );
582   }
583
584   return layoutSize.height;
585 }
586
587 bool Controller::Relayout( const Size& size )
588 {
589   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f\n", this, size.width, size.height );
590
591   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
592   {
593     bool glyphsRemoved( false );
594     if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
595     {
596       mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
597       glyphsRemoved = true;
598     }
599     // Not worth to relayout if width or height is equal to zero.
600     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout (skipped)\n" );
601     return glyphsRemoved;
602   }
603
604   if( size != mImpl->mControlSize )
605   {
606     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", mImpl->mControlSize.width, mImpl->mControlSize.height );
607
608     // Operations that need to be done if the size changes.
609     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
610                                                              LAYOUT                    |
611                                                              ALIGN                     |
612                                                              UPDATE_ACTUAL_SIZE        |
613                                                              REORDER );
614
615     mImpl->mControlSize = size;
616   }
617
618   // Make sure the model is up-to-date before layouting
619   ProcessModifyEvents();
620   mImpl->UpdateModel( mImpl->mOperationsPending );
621
622   Size layoutSize;
623   bool updated = DoRelayout( mImpl->mControlSize,
624                              mImpl->mOperationsPending,
625                              layoutSize );
626
627   // Do not re-do any operation until something changes.
628   mImpl->mOperationsPending = NO_OPERATION;
629
630   // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
631   CalculateTextAlignment( size );
632
633   if( mImpl->mEventData )
634   {
635     // Move the cursor, grab handle etc.
636     updated = mImpl->ProcessInputEvents() || updated;
637   }
638
639   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout\n" );
640   return updated;
641 }
642
643 void Controller::ProcessModifyEvents()
644 {
645   std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
646
647   for( unsigned int i=0; i<events.size(); ++i )
648   {
649     if( ModifyEvent::TEXT_REPLACED == events[0].type )
650     {
651       // A (single) replace event should come first, otherwise we wasted time processing NOOP events
652       DALI_ASSERT_DEBUG( 0 == i && "Unexpected TEXT_REPLACED event" );
653
654       TextReplacedEvent();
655     }
656     else if( ModifyEvent::TEXT_INSERTED == events[0].type )
657     {
658       TextInsertedEvent();
659     }
660     else if( ModifyEvent::TEXT_DELETED == events[0].type )
661     {
662       // Placeholder-text cannot be deleted
663       if( !mImpl->IsShowingPlaceholderText() )
664       {
665         TextDeletedEvent();
666       }
667     }
668   }
669
670   // Discard temporary text
671   events.clear();
672 }
673
674 void Controller::ResetText()
675 {
676   // Reset buffers.
677   mImpl->mLogicalModel->mText.Clear();
678   ClearModelData();
679
680   // We have cleared everything including the placeholder-text
681   mImpl->PlaceholderCleared();
682
683   // The natural size needs to be re-calculated.
684   mImpl->mRecalculateNaturalSize = true;
685
686   // Apply modifications to the model
687   mImpl->mOperationsPending = ALL_OPERATIONS;
688 }
689
690 void Controller::ResetCursorPosition( CharacterIndex cursorIndex )
691 {
692   // Reset the cursor position
693   if( NULL != mImpl->mEventData )
694   {
695     mImpl->mEventData->mPrimaryCursorPosition = cursorIndex;
696
697     // Update the cursor if it's in editing mode.
698     if( ( EventData::EDITING == mImpl->mEventData->mState ) ||
699         ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) )
700     {
701       mImpl->mEventData->mUpdateCursorPosition = true;
702     }
703   }
704 }
705
706 void Controller::ResetScrollPosition()
707 {
708   if( NULL != mImpl->mEventData )
709   {
710     // Reset the scroll position.
711     mImpl->mEventData->mScrollPosition = Vector2::ZERO;
712     mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
713   }
714 }
715
716 void Controller::TextReplacedEvent()
717 {
718   // Reset buffers.
719   ClearModelData();
720
721   // The natural size needs to be re-calculated.
722   mImpl->mRecalculateNaturalSize = true;
723
724   // Apply modifications to the model
725   mImpl->mOperationsPending = ALL_OPERATIONS;
726   mImpl->UpdateModel( ALL_OPERATIONS );
727   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
728                                                            ALIGN              |
729                                                            UPDATE_ACTUAL_SIZE |
730                                                            REORDER );
731 }
732
733 void Controller::TextInsertedEvent()
734 {
735   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextInsertedEvent" );
736
737   // TODO - Optimize this
738   ClearModelData();
739
740   // The natural size needs to be re-calculated.
741   mImpl->mRecalculateNaturalSize = true;
742
743   // Apply modifications to the model; TODO - Optimize this
744   mImpl->mOperationsPending = ALL_OPERATIONS;
745   mImpl->UpdateModel( ALL_OPERATIONS );
746   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
747                                                            ALIGN              |
748                                                            UPDATE_ACTUAL_SIZE |
749                                                            REORDER );
750
751   // Queue a cursor reposition event; this must wait until after DoRelayout()
752   mImpl->mEventData->mUpdateCursorPosition = true;
753   mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
754 }
755
756 void Controller::TextDeletedEvent()
757 {
758   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextDeletedEvent" );
759
760   // TODO - Optimize this
761   ClearModelData();
762
763   // The natural size needs to be re-calculated.
764   mImpl->mRecalculateNaturalSize = true;
765
766   // Apply modifications to the model; TODO - Optimize this
767   mImpl->mOperationsPending = ALL_OPERATIONS;
768   mImpl->UpdateModel( ALL_OPERATIONS );
769   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
770                                                            ALIGN              |
771                                                            UPDATE_ACTUAL_SIZE |
772                                                            REORDER );
773
774   // Queue a cursor reposition event; this must wait until after DoRelayout()
775   mImpl->mEventData->mUpdateCursorPosition = true;
776   mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
777 }
778
779 bool Controller::DoRelayout( const Size& size,
780                              OperationsMask operationsRequired,
781                              Size& layoutSize )
782 {
783   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout %p size %f,%f\n", this, size.width, size.height );
784   bool viewUpdated( false );
785
786   // Calculate the operations to be done.
787   const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
788
789   if( LAYOUT & operations )
790   {
791     // Some vectors with data needed to layout and reorder may be void
792     // after the first time the text has been laid out.
793     // Fill the vectors again.
794
795     Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
796
797     if( 0u == numberOfGlyphs )
798     {
799       // Nothing else to do if there is no glyphs.
800       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout no glyphs, view updated true\n" );
801       return true;
802     }
803
804     Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
805     Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
806     Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
807     Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
808     Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
809     Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
810
811     // Set the layout parameters.
812     LayoutParameters layoutParameters( size,
813                                        mImpl->mLogicalModel->mText.Begin(),
814                                        lineBreakInfo.Begin(),
815                                        wordBreakInfo.Begin(),
816                                        ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
817                                        numberOfGlyphs,
818                                        glyphs.Begin(),
819                                        glyphsToCharactersMap.Begin(),
820                                        charactersPerGlyph.Begin() );
821
822     // The laid-out lines.
823     // It's not possible to know in how many lines the text is going to be laid-out,
824     // but it can be resized at least with the number of 'paragraphs' to avoid
825     // some re-allocations.
826     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
827
828     // Delete any previous laid out lines before setting the new ones.
829     lines.Clear();
830
831     // The capacity of the bidirectional paragraph info is the number of paragraphs.
832     lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
833
834     // Resize the vector of positions to have the same size than the vector of glyphs.
835     Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
836     glyphPositions.Resize( numberOfGlyphs );
837
838     // Update the visual model.
839     viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
840                                                    glyphPositions,
841                                                    lines,
842                                                    layoutSize );
843
844     if( viewUpdated )
845     {
846       // Reorder the lines
847       if( REORDER & operations )
848       {
849         Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
850
851         // Check first if there are paragraphs with bidirectional info.
852         if( 0u != bidirectionalInfo.Count() )
853         {
854           // Get the lines
855           const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
856
857           // Reorder the lines.
858           Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
859           lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
860           ReorderLines( bidirectionalInfo,
861                         lines,
862                         lineBidirectionalInfoRuns );
863
864           // Set the bidirectional info into the model.
865           const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
866           mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
867                                                        numberOfBidirectionalInfoRuns );
868
869           // Set the bidirectional info per line into the layout parameters.
870           layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
871           layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
872
873           // Get the character to glyph conversion table and set into the layout.
874           layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
875
876           // Get the glyphs per character table and set into the layout.
877           layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
878
879           // Re-layout the text. Reorder those lines with right to left characters.
880           mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
881                                                          glyphPositions );
882
883           // Free the allocated memory used to store the conversion table in the bidirectional line info run.
884           for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
885                  endIt = lineBidirectionalInfoRuns.End();
886                it != endIt;
887                ++it )
888           {
889             BidirectionalLineInfoRun& bidiLineInfo = *it;
890
891             free( bidiLineInfo.visualToLogicalMap );
892           }
893         }
894       } // REORDER
895
896       // Sets the actual size.
897       if( UPDATE_ACTUAL_SIZE & operations )
898       {
899         mImpl->mVisualModel->SetActualSize( layoutSize );
900       }
901     } // view updated
902   }
903   else
904   {
905     layoutSize = mImpl->mVisualModel->GetActualSize();
906   }
907
908   if( ALIGN & operations )
909   {
910     // The laid-out lines.
911     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
912
913     mImpl->mLayoutEngine.Align( layoutSize,
914                                 lines );
915
916     viewUpdated = true;
917   }
918
919   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
920   return viewUpdated;
921 }
922
923 void Controller::SetMultiLineEnabled( bool enable )
924 {
925   const LayoutEngine::Layout layout = enable ? LayoutEngine::MULTI_LINE_BOX : LayoutEngine::SINGLE_LINE_BOX;
926
927   if( layout != mImpl->mLayoutEngine.GetLayout() )
928   {
929     // Set the layout type.
930     mImpl->mLayoutEngine.SetLayout( layout );
931
932     // Set the flags to redo the layout operations
933     const OperationsMask layoutOperations =  static_cast<OperationsMask>( LAYOUT             |
934                                                                           UPDATE_ACTUAL_SIZE |
935                                                                           ALIGN              |
936                                                                           REORDER );
937
938     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
939
940     mImpl->RequestRelayout();
941   }
942 }
943
944 bool Controller::IsMultiLineEnabled() const
945 {
946   return LayoutEngine::MULTI_LINE_BOX == mImpl->mLayoutEngine.GetLayout();
947 }
948
949 void Controller::SetHorizontalAlignment( LayoutEngine::HorizontalAlignment alignment )
950 {
951   if( alignment != mImpl->mLayoutEngine.GetHorizontalAlignment() )
952   {
953     // Set the alignment.
954     mImpl->mLayoutEngine.SetHorizontalAlignment( alignment );
955
956     // Set the flag to redo the alignment operation.
957     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
958
959     mImpl->RequestRelayout();
960   }
961 }
962
963 LayoutEngine::HorizontalAlignment Controller::GetHorizontalAlignment() const
964 {
965   return mImpl->mLayoutEngine.GetHorizontalAlignment();
966 }
967
968 void Controller::SetVerticalAlignment( LayoutEngine::VerticalAlignment alignment )
969 {
970   if( alignment != mImpl->mLayoutEngine.GetVerticalAlignment() )
971   {
972     // Set the alignment.
973     mImpl->mLayoutEngine.SetVerticalAlignment( alignment );
974
975     // Set the flag to redo the alignment operation.
976     // TODO : Is not needed re-layout and reorder again but with the current implementation it is.
977     //        Im working on a different patch to fix an issue with the alignment. When that patch
978     //        is in, this issue can be fixed.
979     const OperationsMask layoutOperations =  static_cast<OperationsMask>( LAYOUT             |
980                                                                           UPDATE_ACTUAL_SIZE |
981                                                                           ALIGN              |
982                                                                           REORDER );
983
984     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
985
986     mImpl->RequestRelayout();
987   }
988 }
989
990 LayoutEngine::VerticalAlignment Controller::GetVerticalAlignment() const
991 {
992   return mImpl->mLayoutEngine.GetVerticalAlignment();
993 }
994
995 void Controller::CalculateTextAlignment( const Size& size )
996 {
997   // Get the direction of the first character.
998   const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
999
1000   const Size& actualSize = mImpl->mVisualModel->GetActualSize();
1001
1002   // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1003   LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1004   if( firstParagraphDirection &&
1005       ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
1006   {
1007     if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
1008     {
1009       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1010     }
1011     else
1012     {
1013       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1014     }
1015   }
1016
1017   switch( horizontalAlignment )
1018   {
1019     case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1020     {
1021       mImpl->mAlignmentOffset.x = 0.f;
1022       break;
1023     }
1024     case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1025     {
1026       const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1027       mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1028       break;
1029     }
1030     case LayoutEngine::HORIZONTAL_ALIGN_END:
1031     {
1032       mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1033       break;
1034     }
1035   }
1036
1037   const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1038   switch( verticalAlignment )
1039   {
1040     case LayoutEngine::VERTICAL_ALIGN_TOP:
1041     {
1042       mImpl->mAlignmentOffset.y = 0.f;
1043       break;
1044     }
1045     case LayoutEngine::VERTICAL_ALIGN_CENTER:
1046     {
1047       const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1048       mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
1049       break;
1050     }
1051     case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1052     {
1053       mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1054       break;
1055     }
1056   }
1057 }
1058
1059 LayoutEngine& Controller::GetLayoutEngine()
1060 {
1061   return mImpl->mLayoutEngine;
1062 }
1063
1064 View& Controller::GetView()
1065 {
1066   return mImpl->mView;
1067 }
1068
1069 void Controller::KeyboardFocusGainEvent()
1070 {
1071   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
1072
1073   if( mImpl->mEventData )
1074   {
1075     mImpl->ChangeState( EventData::EDITING );
1076
1077     if( mImpl->IsShowingPlaceholderText() )
1078     {
1079       // Show alternative placeholder-text when editing
1080       ShowPlaceholderText();
1081     }
1082
1083     mImpl->RequestRelayout();
1084   }
1085 }
1086
1087 void Controller::KeyboardFocusLostEvent()
1088 {
1089   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
1090
1091   if( mImpl->mEventData )
1092   {
1093     mImpl->ChangeState( EventData::INACTIVE );
1094
1095     if( mImpl->IsShowingPlaceholderText() )
1096     {
1097       // Revert to regular placeholder-text when not editing
1098       ShowPlaceholderText();
1099     }
1100
1101     mImpl->RequestRelayout();
1102   }
1103 }
1104
1105 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1106 {
1107   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
1108
1109   bool textChanged( false );
1110
1111   if( mImpl->mEventData &&
1112       keyEvent.state == KeyEvent::Down )
1113   {
1114     int keyCode = keyEvent.keyCode;
1115     const std::string& keyString = keyEvent.keyPressed;
1116
1117     // Pre-process to separate modifying events from non-modifying input events.
1118     if( Dali::DALI_KEY_ESCAPE == keyCode )
1119     {
1120       // Escape key is a special case which causes focus loss
1121       KeyboardFocusLostEvent();
1122     }
1123     else if( Dali::DALI_KEY_CURSOR_LEFT  == keyCode ||
1124              Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1125              Dali::DALI_KEY_CURSOR_UP    == keyCode ||
1126              Dali::DALI_KEY_CURSOR_DOWN  == keyCode )
1127     {
1128       Event event( Event::CURSOR_KEY_EVENT );
1129       event.p1.mInt = keyCode;
1130       mImpl->mEventData->mEventQueue.push_back( event );
1131     }
1132     else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1133     {
1134       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE\n", this );
1135
1136       // IMF manager is no longer handling key-events
1137       mImpl->ClearPreEditFlag();
1138
1139       // Remove the character before the current cursor position
1140       bool removed = RemoveText( -1, 1 );
1141
1142       if( removed )
1143       {
1144         if( 0u == mImpl->mLogicalModel->mText.Count() )
1145         {
1146           ShowPlaceholderText();
1147           mImpl->mEventData->mUpdateCursorPosition = true;
1148         }
1149         else
1150         {
1151           mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1152         }
1153
1154         textChanged = true;
1155       }
1156     }
1157     else
1158     {
1159       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
1160
1161       // IMF manager is no longer handling key-events
1162       mImpl->ClearPreEditFlag();
1163
1164       InsertText( keyString, COMMIT );
1165
1166       textChanged = true;
1167     }
1168
1169     mImpl->ChangeState( EventData::EDITING ); // todo Confirm this is the best place to change the state of
1170
1171     mImpl->RequestRelayout();
1172   }
1173
1174   if( textChanged )
1175   {
1176     // Do this last since it provides callbacks into application code
1177     mImpl->mControlInterface.TextChanged();
1178   }
1179
1180   return false;
1181 }
1182
1183 void Controller::InsertText( const std::string& text, Controller::InsertType type )
1184 {
1185   bool removedPreEdit( false );
1186   bool maxLengthReached( false );
1187
1188   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
1189   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n",
1190                  this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"),
1191                  mImpl->mEventData->mPrimaryCursorPosition, mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1192
1193   Vector<Character> utf32Characters;
1194   Length characterCount( 0u );
1195
1196   if( ! text.empty() )
1197   {
1198     // The placeholder text is no longer needed
1199     if( mImpl->IsShowingPlaceholderText() )
1200     {
1201       ResetText();
1202     }
1203
1204     //  Convert text into UTF-32
1205     utf32Characters.Resize( text.size() );
1206
1207     // This is a bit horrible but std::string returns a (signed) char*
1208     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1209
1210     // Transform a text array encoded in utf8 into an array encoded in utf32.
1211     // It returns the actual number of characters.
1212     characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1213     utf32Characters.Resize( characterCount );
1214
1215     DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
1216     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
1217   }
1218
1219   if( 0u != utf32Characters.Count() )
1220   {
1221     // Handle the IMF (predicitive text) state changes
1222     if( mImpl->mEventData )
1223     {
1224       if( mImpl->mEventData->mPreEditFlag &&
1225           0 != mImpl->mEventData->mPreEditLength )
1226       {
1227         // Remove previous pre-edit text
1228         CharacterIndex offset = mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition;
1229         removedPreEdit = RemoveText( -static_cast<int>(offset), mImpl->mEventData->mPreEditLength );
1230
1231         mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
1232         mImpl->mEventData->mPreEditLength = 0;
1233       }
1234
1235       if( COMMIT == type )
1236       {
1237         // IMF manager is no longer handling key-events
1238         mImpl->ClearPreEditFlag();
1239       }
1240       else // PRE_EDIT
1241       {
1242         if( ! mImpl->mEventData->mPreEditFlag )
1243         {
1244           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Entered PreEdit state" );
1245
1246           // Record the start of the pre-edit text
1247           mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
1248         }
1249
1250         mImpl->mEventData->mPreEditLength = utf32Characters.Count();
1251         mImpl->mEventData->mPreEditFlag = true;
1252
1253         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1254       }
1255     }
1256
1257     const Length numberOfCharactersInModel = mImpl->mLogicalModel->GetNumberOfCharacters();
1258
1259     // Restrict new text to fit within Maximum characters setting
1260     Length maxSizeOfNewText = std::min ( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
1261     maxLengthReached = ( characterCount > maxSizeOfNewText );
1262
1263     // Insert at current cursor position
1264     CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
1265
1266     Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1267
1268     if( cursorIndex < numberOfCharactersInModel )
1269     {
1270       modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1271     }
1272     else
1273     {
1274       modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1275     }
1276
1277     cursorIndex += maxSizeOfNewText;
1278
1279     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
1280   }
1281
1282   if( removedPreEdit ||
1283       0 != utf32Characters.Count() )
1284   {
1285     // Queue an inserted event
1286     mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
1287   }
1288
1289   if( maxLengthReached )
1290   {
1291     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mLogicalModel->mText.Count() );
1292
1293     mImpl->ResetImfManager();
1294
1295     // Do this last since it provides callbacks into application code
1296     mImpl->mControlInterface.MaxLengthReached();
1297   }
1298 }
1299
1300 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1301 {
1302   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
1303
1304   if( NULL != mImpl->mEventData )
1305   {
1306     if( 1u == tapCount )
1307     {
1308       bool tapDuringEditMode( EventData::EDITING == mImpl->mEventData->mState );
1309
1310       if( ! mImpl->IsShowingPlaceholderText() &&
1311           EventData::EDITING == mImpl->mEventData->mState )
1312       {
1313         // Grab handle is not shown until a tap is received whilst EDITING
1314         if( tapDuringEditMode )
1315         {
1316           mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1317         }
1318         mImpl->mEventData->mDecorator->SetPopupActive( false );
1319       }
1320
1321       mImpl->ChangeState( EventData::EDITING );
1322     }
1323     else if( mImpl->mEventData->mSelectionEnabled &&
1324              ( 2u == tapCount ) )
1325     {
1326       mImpl->ChangeState( EventData::SELECTING );
1327     }
1328   }
1329
1330   // Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
1331   if( mImpl->mEventData )
1332   {
1333     Event event( Event::TAP_EVENT );
1334     event.p1.mUint = tapCount;
1335     event.p2.mFloat = x;
1336     event.p3.mFloat = y;
1337     mImpl->mEventData->mEventQueue.push_back( event );
1338
1339     mImpl->RequestRelayout();
1340   }
1341
1342   // Reset keyboard as tap event has occurred.
1343   mImpl->ResetImfManager();
1344 }
1345
1346 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1347 {
1348   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
1349
1350   if( mImpl->mEventData )
1351   {
1352     Event event( Event::PAN_EVENT );
1353     event.p1.mInt = state;
1354     event.p2.mFloat = displacement.x;
1355     event.p3.mFloat = displacement.y;
1356     mImpl->mEventData->mEventQueue.push_back( event );
1357
1358     mImpl->RequestRelayout();
1359   }
1360 }
1361
1362 void Controller::GetTargetSize( Vector2& targetSize )
1363 {
1364   targetSize = mImpl->mControlSize;
1365 }
1366
1367 void Controller::AddDecoration( Actor& actor )
1368 {
1369   mImpl->mControlInterface.AddDecoration( actor );
1370 }
1371
1372 void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
1373 {
1374   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected DecorationEvent" );
1375
1376   if( mImpl->mEventData )
1377   {
1378     switch( handleType )
1379     {
1380       case GRAB_HANDLE:
1381       {
1382         Event event( Event::GRAB_HANDLE_EVENT );
1383         event.p1.mUint  = state;
1384         event.p2.mFloat = x;
1385         event.p3.mFloat = y;
1386
1387         mImpl->mEventData->mEventQueue.push_back( event );
1388         break;
1389       }
1390       case LEFT_SELECTION_HANDLE:
1391       {
1392         Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
1393         event.p1.mUint  = state;
1394         event.p2.mFloat = x;
1395         event.p3.mFloat = y;
1396
1397         mImpl->mEventData->mEventQueue.push_back( event );
1398         break;
1399       }
1400       case RIGHT_SELECTION_HANDLE:
1401       {
1402         Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
1403         event.p1.mUint  = state;
1404         event.p2.mFloat = x;
1405         event.p3.mFloat = y;
1406
1407         mImpl->mEventData->mEventQueue.push_back( event );
1408         break;
1409       }
1410       case HANDLE_TYPE_COUNT:
1411       {
1412         DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
1413       }
1414     }
1415
1416     mImpl->RequestRelayout();
1417   }
1418 }
1419
1420 ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
1421 {
1422   bool update( false );
1423   bool requestRelayout = false;
1424
1425   std::string text;
1426   unsigned int cursorPosition( 0 );
1427
1428   switch ( imfEvent.eventName )
1429   {
1430     case ImfManager::COMMIT:
1431     {
1432       InsertText( imfEvent.predictiveString, Text::Controller::COMMIT );
1433       requestRelayout = true;
1434       break;
1435     }
1436     case ImfManager::PREEDIT:
1437     {
1438       InsertText( imfEvent.predictiveString, Text::Controller::PRE_EDIT );
1439       update = true;
1440       requestRelayout = true;
1441       break;
1442     }
1443     case ImfManager::DELETESURROUNDING:
1444     {
1445       RemoveText( imfEvent.cursorOffset, imfEvent.numberOfChars );
1446       requestRelayout = true;
1447       break;
1448     }
1449     case ImfManager::GETSURROUNDING:
1450     {
1451       GetText( text );
1452       cursorPosition = GetLogicalCursorPosition();
1453
1454       imfManager.SetSurroundingText( text );
1455       imfManager.SetCursorPosition( cursorPosition );
1456       break;
1457     }
1458     case ImfManager::VOID:
1459     {
1460       // do nothing
1461       break;
1462     }
1463   } // end switch
1464
1465   if( ImfManager::GETSURROUNDING != imfEvent.eventName )
1466   {
1467     GetText( text );
1468     cursorPosition = GetLogicalCursorPosition();
1469   }
1470
1471   if( requestRelayout )
1472   {
1473     mImpl->mOperationsPending = ALL_OPERATIONS;
1474     mImpl->RequestRelayout();
1475
1476     // Do this last since it provides callbacks into application code
1477     mImpl->mControlInterface.TextChanged();
1478   }
1479
1480   ImfManager::ImfCallbackData callbackData( update, cursorPosition, text, false );
1481
1482   return callbackData;
1483 }
1484
1485
1486 Controller::~Controller()
1487 {
1488   delete mImpl;
1489 }
1490
1491 void Controller::ShowPlaceholderText()
1492 {
1493   if( mImpl->IsPlaceholderAvailable() )
1494   {
1495     DALI_ASSERT_DEBUG( mImpl->mEventData && "No placeholder text available" );
1496
1497     mImpl->mEventData->mIsShowingPlaceholderText = true;
1498
1499     // Disable handles when showing place-holder text
1500     mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1501     mImpl->mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1502     mImpl->mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1503
1504     const char* text( NULL );
1505     size_t size( 0 );
1506
1507     // TODO - Switch placeholder text styles when changing state
1508     if( EventData::INACTIVE != mImpl->mEventData->mState &&
1509         0u != mImpl->mEventData->mPlaceholderTextActive.c_str() )
1510     {
1511       text = mImpl->mEventData->mPlaceholderTextActive.c_str();
1512       size = mImpl->mEventData->mPlaceholderTextActive.size();
1513     }
1514     else
1515     {
1516       text = mImpl->mEventData->mPlaceholderTextInactive.c_str();
1517       size = mImpl->mEventData->mPlaceholderTextInactive.size();
1518     }
1519
1520     // Reset model for showing placeholder.
1521     mImpl->mLogicalModel->mText.Clear();
1522     ClearModelData();
1523     mImpl->mVisualModel->SetTextColor( mImpl->mEventData->mPlaceholderTextColor );
1524
1525     // Convert text into UTF-32
1526     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1527     utf32Characters.Resize( size );
1528
1529     // This is a bit horrible but std::string returns a (signed) char*
1530     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text );
1531
1532     // Transform a text array encoded in utf8 into an array encoded in utf32.
1533     // It returns the actual number of characters.
1534     Length characterCount = Utf8ToUtf32( utf8, size, utf32Characters.Begin() );
1535     utf32Characters.Resize( characterCount );
1536
1537     // Reset the cursor position
1538     mImpl->mEventData->mPrimaryCursorPosition = 0;
1539
1540     // The natural size needs to be re-calculated.
1541     mImpl->mRecalculateNaturalSize = true;
1542
1543     // Apply modifications to the model
1544     mImpl->mOperationsPending = ALL_OPERATIONS;
1545
1546     // Update the rest of the model during size negotiation
1547     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
1548   }
1549 }
1550
1551 void Controller::ClearModelData()
1552 {
1553   // n.b. This does not Clear the mText from mLogicalModel
1554   mImpl->mLogicalModel->mScriptRuns.Clear();
1555   mImpl->mLogicalModel->mFontRuns.Clear();
1556   mImpl->mLogicalModel->mLineBreakInfo.Clear();
1557   mImpl->mLogicalModel->mWordBreakInfo.Clear();
1558   mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1559   mImpl->mLogicalModel->mCharacterDirections.Clear();
1560   mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1561   mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1562   mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1563   mImpl->mVisualModel->mGlyphs.Clear();
1564   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1565   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1566   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1567   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1568   mImpl->mVisualModel->mGlyphPositions.Clear();
1569   mImpl->mVisualModel->mLines.Clear();
1570   mImpl->mVisualModel->ClearCaches();
1571 }
1572
1573 void Controller::ClearFontData()
1574 {
1575   mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1576   mImpl->mLogicalModel->mFontRuns.Clear();
1577   mImpl->mVisualModel->mGlyphs.Clear();
1578   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1579   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1580   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1581   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1582   mImpl->mVisualModel->mGlyphPositions.Clear();
1583   mImpl->mVisualModel->mLines.Clear();
1584   mImpl->mVisualModel->ClearCaches();
1585 }
1586
1587 Controller::Controller( ControlInterface& controlInterface )
1588 : mImpl( NULL )
1589 {
1590   mImpl = new Controller::Impl( controlInterface );
1591 }
1592
1593 } // namespace Text
1594
1595 } // namespace Toolkit
1596
1597 } // namespace Dali