Set label padding in case of ResizePolicy::USE_NATURAL_SIZE
[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   // Cancel previously queued inserts etc.
77   mImpl->mModifyEvents.clear();
78
79   // Remove the previously set text
80   ResetText();
81
82   if( ! text.empty() )
83   {
84     //  Convert text into UTF-32
85     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
86     utf32Characters.Resize( text.size() );
87
88     // This is a bit horrible but std::string returns a (signed) char*
89     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
90
91     // Transform a text array encoded in utf8 into an array encoded in utf32.
92     // It returns the actual number of characters.
93     Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
94     utf32Characters.Resize( characterCount );
95
96     DALI_ASSERT_DEBUG( text.size() >= characterCount && "Invalid UTF32 conversion length" );
97     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText %p UTF8 size %d, UTF32 size %d\n", this, text.size(), mImpl->mLogicalModel->mText.Count() );
98
99     // Reset the cursor position
100     if( mImpl->mEventData )
101     {
102       mImpl->mEventData->mPrimaryCursorPosition = characterCount;
103     }
104
105     // Update the rest of the model during size negotiation
106     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
107   }
108   else
109   {
110     ShowPlaceholderText();
111   }
112
113   if( mImpl->mEventData )
114   {
115     // Cancel previously queued events
116     mImpl->mEventData->mEventQueue.clear();
117   }
118
119   // Reset keyboard as text changed
120   mImpl->PreEditReset();
121 }
122
123 void Controller::GetText( std::string& text ) const
124 {
125   if( ! mImpl->IsShowingPlaceholderText() )
126   {
127     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
128
129     if( 0u != utf32Characters.Count() )
130     {
131       uint32_t numberOfBytes = GetNumberOfUtf8Bytes( &utf32Characters[0], utf32Characters.Count() );
132
133       text.resize( numberOfBytes );
134
135       // This is a bit horrible but std::string returns a (signed) char*
136       Utf32ToUtf8( &utf32Characters[0], utf32Characters.Count(), reinterpret_cast<uint8_t*>(&text[0]) );
137     }
138   }
139   else
140   {
141     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::GetText %p empty (but showing placeholder)\n", this );
142   }
143 }
144
145 unsigned int Controller::GetLogicalCursorPosition() const
146 {
147   if( mImpl->mEventData )
148   {
149     return mImpl->mEventData->mPrimaryCursorPosition;
150   }
151
152   return 0u;
153 }
154
155 void Controller::SetPlaceholderText( PlaceholderType type, const std::string& text )
156 {
157   if( mImpl->mEventData )
158   {
159     if( PLACEHOLDER_TYPE_INACTIVE == type )
160     {
161       mImpl->mEventData->mPlaceholderTextInactive = text;
162     }
163     else
164     {
165       mImpl->mEventData->mPlaceholderTextActive = text;
166     }
167
168     ShowPlaceholderText();
169   }
170 }
171
172 void Controller::GetPlaceholderText( PlaceholderType type, std::string& text ) const
173 {
174   if( mImpl->mEventData )
175   {
176     if( PLACEHOLDER_TYPE_INACTIVE == type )
177     {
178       text = mImpl->mEventData->mPlaceholderTextInactive;
179     }
180     else
181     {
182       text = mImpl->mEventData->mPlaceholderTextActive;
183     }
184   }
185 }
186
187 void Controller::SetMaximumNumberOfCharacters( int maxCharacters )
188 {
189   if ( maxCharacters >= 0 )
190   {
191     mImpl->mMaximumNumberOfCharacters = maxCharacters;
192   }
193 }
194
195 int Controller::GetMaximumNumberOfCharacters()
196 {
197   return mImpl->mMaximumNumberOfCharacters;
198 }
199
200 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
201 {
202   if( !mImpl->mFontDefaults )
203   {
204     mImpl->mFontDefaults = new FontDefaults();
205   }
206
207   mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
208
209   // Clear the font-specific data
210   ClearFontData();
211
212   mImpl->mOperationsPending = ALL_OPERATIONS;
213   mImpl->mRecalculateNaturalSize = true;
214
215   mImpl->RequestRelayout();
216 }
217
218 const std::string& Controller::GetDefaultFontFamily() const
219 {
220   if( mImpl->mFontDefaults )
221   {
222     return mImpl->mFontDefaults->mDefaultFontFamily;
223   }
224
225   return EMPTY_STRING;
226 }
227
228 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
229 {
230   if( !mImpl->mFontDefaults )
231   {
232     mImpl->mFontDefaults = new FontDefaults();
233   }
234
235   mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
236
237   // Clear the font-specific data
238   ClearFontData();
239
240   mImpl->mOperationsPending = ALL_OPERATIONS;
241   mImpl->mRecalculateNaturalSize = true;
242
243   mImpl->RequestRelayout();
244 }
245
246 const std::string& Controller::GetDefaultFontStyle() const
247 {
248   if( mImpl->mFontDefaults )
249   {
250     return mImpl->mFontDefaults->mDefaultFontStyle;
251   }
252
253   return EMPTY_STRING;
254 }
255
256 void Controller::SetDefaultPointSize( float pointSize )
257 {
258   if( !mImpl->mFontDefaults )
259   {
260     mImpl->mFontDefaults = new FontDefaults();
261   }
262
263   mImpl->mFontDefaults->mDefaultPointSize = pointSize;
264
265   // Clear the font-specific data
266   ClearFontData();
267
268   mImpl->mOperationsPending = ALL_OPERATIONS;
269   mImpl->mRecalculateNaturalSize = true;
270
271   mImpl->RequestRelayout();
272 }
273
274 float Controller::GetDefaultPointSize() const
275 {
276   if( mImpl->mFontDefaults )
277   {
278     return mImpl->mFontDefaults->mDefaultPointSize;
279   }
280
281   return 0.0f;
282 }
283
284 void Controller::SetTextColor( const Vector4& textColor )
285 {
286   mImpl->mTextColor = textColor;
287
288   if( ! mImpl->IsShowingPlaceholderText() )
289   {
290     mImpl->mVisualModel->SetTextColor( textColor );
291   }
292 }
293
294 const Vector4& Controller::GetTextColor() const
295 {
296   return mImpl->mTextColor;
297 }
298
299 bool Controller::RemoveText( int cursorOffset, int numberOfChars )
300 {
301   bool removed( false );
302
303   DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfChars %d\n",
304                  this, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition, cursorOffset, numberOfChars );
305
306   if( ! mImpl->IsShowingPlaceholderText() )
307   {
308     // Delete at current cursor position
309     Vector<Character>& currentText = mImpl->mLogicalModel->mText;
310     CharacterIndex& oldCursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
311
312     CharacterIndex cursorIndex = oldCursorIndex;
313
314     // Validate the cursor position & number of characters
315     if( static_cast< CharacterIndex >( std::abs( cursorOffset ) ) <= cursorIndex )
316     {
317       cursorIndex = oldCursorIndex + cursorOffset;
318     }
319
320     if( (cursorIndex + numberOfChars) > currentText.Count() )
321     {
322       numberOfChars = currentText.Count() - cursorIndex;
323     }
324
325     if( cursorIndex >= 0 &&
326         (cursorIndex + numberOfChars) <= currentText.Count() )
327     {
328       Vector<Character>::Iterator first = currentText.Begin() + cursorIndex;
329       Vector<Character>::Iterator last  = first + numberOfChars;
330
331       currentText.Erase( first, last );
332
333       // Cursor position retreat
334       oldCursorIndex = cursorIndex;
335
336       DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", this, numberOfChars );
337       removed = true;
338     }
339   }
340
341   return removed;
342 }
343
344 void Controller::SetPlaceholderTextColor( const Vector4& textColor )
345 {
346   if( mImpl->mEventData )
347   {
348     mImpl->mEventData->mPlaceholderTextColor = textColor;
349   }
350
351   if( mImpl->IsShowingPlaceholderText() )
352   {
353     mImpl->mVisualModel->SetTextColor( textColor );
354   }
355 }
356
357 const Vector4& Controller::GetPlaceholderTextColor() const
358 {
359   if( mImpl->mEventData )
360   {
361     return mImpl->mEventData->mPlaceholderTextColor;
362   }
363
364   return Color::BLACK;
365 }
366
367 void Controller::SetShadowOffset( const Vector2& shadowOffset )
368 {
369   mImpl->mVisualModel->SetShadowOffset( shadowOffset );
370 }
371
372 const Vector2& Controller::GetShadowOffset() const
373 {
374   return mImpl->mVisualModel->GetShadowOffset();
375 }
376
377 void Controller::SetShadowColor( const Vector4& shadowColor )
378 {
379   mImpl->mVisualModel->SetShadowColor( shadowColor );
380 }
381
382 const Vector4& Controller::GetShadowColor() const
383 {
384   return mImpl->mVisualModel->GetShadowColor();
385 }
386
387 void Controller::SetUnderlineColor( const Vector4& color )
388 {
389   mImpl->mVisualModel->SetUnderlineColor( color );
390 }
391
392 const Vector4& Controller::GetUnderlineColor() const
393 {
394   return mImpl->mVisualModel->GetUnderlineColor();
395 }
396
397 void Controller::SetUnderlineEnabled( bool enabled )
398 {
399   mImpl->mVisualModel->SetUnderlineEnabled( enabled );
400 }
401
402 bool Controller::IsUnderlineEnabled() const
403 {
404   return mImpl->mVisualModel->IsUnderlineEnabled();
405 }
406
407 void Controller::SetUnderlineHeight( float height )
408 {
409   mImpl->mVisualModel->SetUnderlineHeight( height );
410 }
411
412 float Controller::GetUnderlineHeight() const
413 {
414   return mImpl->mVisualModel->GetUnderlineHeight();
415 }
416
417 void Controller::SetEnableCursorBlink( bool enable )
418 {
419   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "TextInput disabled" );
420
421   if( mImpl->mEventData )
422   {
423     mImpl->mEventData->mCursorBlinkEnabled = enable;
424
425     if( !enable &&
426         mImpl->mEventData->mDecorator )
427     {
428       mImpl->mEventData->mDecorator->StopCursorBlink();
429     }
430   }
431 }
432
433 bool Controller::GetEnableCursorBlink() const
434 {
435   if( mImpl->mEventData )
436   {
437     return mImpl->mEventData->mCursorBlinkEnabled;
438   }
439
440   return false;
441 }
442
443 const Vector2& Controller::GetScrollPosition() const
444 {
445   if( mImpl->mEventData )
446   {
447     return mImpl->mEventData->mScrollPosition;
448   }
449
450   return Vector2::ZERO;
451 }
452
453 const Vector2& Controller::GetAlignmentOffset() const
454 {
455   return mImpl->mAlignmentOffset;
456 }
457
458 Vector3 Controller::GetNaturalSize()
459 {
460   Vector3 naturalSize;
461
462   // Make sure the model is up-to-date before layouting
463   ProcessModifyEvents();
464
465   if( mImpl->mRecalculateNaturalSize )
466   {
467     // Operations that can be done only once until the text changes.
468     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
469                                                                            GET_SCRIPTS       |
470                                                                            VALIDATE_FONTS    |
471                                                                            GET_LINE_BREAKS   |
472                                                                            GET_WORD_BREAKS   |
473                                                                            BIDI_INFO         |
474                                                                            SHAPE_TEXT        |
475                                                                            GET_GLYPH_METRICS );
476     // Make sure the model is up-to-date before layouting
477     mImpl->UpdateModel( onlyOnceOperations );
478
479     // Operations that need to be done if the size changes.
480     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
481                                                                         ALIGN  |
482                                                                         REORDER );
483
484     DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
485                 static_cast<OperationsMask>( onlyOnceOperations |
486                                              sizeOperations ),
487                 naturalSize.GetVectorXY() );
488
489     // Do not do again the only once operations.
490     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
491
492     // Do the size related operations again.
493     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
494
495     // Stores the natural size to avoid recalculate it again
496     // unless the text/style changes.
497     mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
498
499     mImpl->mRecalculateNaturalSize = false;
500   }
501   else
502   {
503     naturalSize = mImpl->mVisualModel->GetNaturalSize();
504   }
505
506   return naturalSize;
507 }
508
509 float Controller::GetHeightForWidth( float width )
510 {
511   // Make sure the model is up-to-date before layouting
512   ProcessModifyEvents();
513
514   Size layoutSize;
515   if( width != mImpl->mControlSize.width )
516   {
517     // Operations that can be done only once until the text changes.
518     const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32  |
519                                                                            GET_SCRIPTS       |
520                                                                            VALIDATE_FONTS    |
521                                                                            GET_LINE_BREAKS   |
522                                                                            GET_WORD_BREAKS   |
523                                                                            BIDI_INFO         |
524                                                                            SHAPE_TEXT        |
525                                                                            GET_GLYPH_METRICS );
526     // Make sure the model is up-to-date before layouting
527     mImpl->UpdateModel( onlyOnceOperations );
528
529     // Operations that need to be done if the size changes.
530     const OperationsMask sizeOperations =  static_cast<OperationsMask>( LAYOUT |
531                                                                         ALIGN  |
532                                                                         REORDER );
533
534     DoRelayout( Size( width, MAX_FLOAT ),
535                 static_cast<OperationsMask>( onlyOnceOperations |
536                                              sizeOperations ),
537                 layoutSize );
538
539     // Do not do again the only once operations.
540     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
541
542     // Do the size related operations again.
543     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
544   }
545   else
546   {
547     layoutSize = mImpl->mVisualModel->GetActualSize();
548   }
549
550   return layoutSize.height;
551 }
552
553 bool Controller::Relayout( const Size& size )
554 {
555   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::Relayout %p --> size %f,%f\n", this, size.width, size.height );
556
557   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
558   {
559     bool glyphsRemoved( false );
560     if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
561     {
562       mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
563       glyphsRemoved = true;
564     }
565     // Not worth to relayout if width or height is equal to zero.
566     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::Relayout <-- (skipped)\n" );
567     return glyphsRemoved;
568   }
569
570   if( size != mImpl->mControlSize )
571   {
572     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", mImpl->mControlSize.width, mImpl->mControlSize.height );
573
574     // Operations that need to be done if the size changes.
575     mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
576                                                              LAYOUT                    |
577                                                              ALIGN                     |
578                                                              UPDATE_ACTUAL_SIZE        |
579                                                              REORDER );
580
581     mImpl->mControlSize = size;
582   }
583
584   // Make sure the model is up-to-date before layouting
585   ProcessModifyEvents();
586   mImpl->UpdateModel( mImpl->mOperationsPending );
587
588   Size layoutSize;
589   bool updated = DoRelayout( mImpl->mControlSize,
590                              mImpl->mOperationsPending,
591                              layoutSize );
592
593   // Do not re-do any operation until something changes.
594   mImpl->mOperationsPending = NO_OPERATION;
595
596   // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
597   CalculateTextAlignment( size );
598
599   if( mImpl->mEventData )
600   {
601     // Move the cursor, grab handle etc.
602     updated = mImpl->ProcessInputEvents() || updated;
603   }
604
605   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::Relayout <--\n" );
606   return updated;
607 }
608
609 void Controller::ProcessModifyEvents()
610 {
611   std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
612
613   for( unsigned int i=0; i<events.size(); ++i )
614   {
615     if( ModifyEvent::TEXT_REPLACED == events[0].type )
616     {
617       // A (single) replace event should come first, otherwise we wasted time processing NOOP events
618       DALI_ASSERT_DEBUG( 0 == i && "Unexpected TEXT_REPLACED event" );
619
620       TextReplacedEvent();
621     }
622     else if( ModifyEvent::TEXT_INSERTED == events[0].type )
623     {
624       TextInsertedEvent();
625     }
626     else if( ModifyEvent::TEXT_DELETED == events[0].type )
627     {
628       // Placeholder-text cannot be deleted
629       if( !mImpl->IsShowingPlaceholderText() )
630       {
631         TextDeletedEvent();
632       }
633     }
634   }
635
636   // Discard temporary text
637   events.clear();
638 }
639
640 void Controller::ResetText()
641 {
642   // Reset buffers.
643   mImpl->mLogicalModel->mText.Clear();
644   ClearModelData();
645
646   // Reset the cursor position
647   if( mImpl->mEventData )
648   {
649     mImpl->mEventData->mPrimaryCursorPosition = 0;
650   }
651
652   // We have cleared everything including the placeholder-text
653   mImpl->PlaceholderCleared();
654
655   // The natural size needs to be re-calculated.
656   mImpl->mRecalculateNaturalSize = true;
657
658   // Apply modifications to the model
659   mImpl->mOperationsPending = ALL_OPERATIONS;
660 }
661
662 void Controller::TextReplacedEvent()
663 {
664   // Reset buffers.
665   ClearModelData();
666
667   // The natural size needs to be re-calculated.
668   mImpl->mRecalculateNaturalSize = true;
669
670   // Apply modifications to the model
671   mImpl->mOperationsPending = ALL_OPERATIONS;
672   mImpl->UpdateModel( ALL_OPERATIONS );
673   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
674                                                            ALIGN              |
675                                                            UPDATE_ACTUAL_SIZE |
676                                                            REORDER );
677 }
678
679 void Controller::TextInsertedEvent()
680 {
681   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextInsertedEvent" );
682
683   // TODO - Optimize this
684   ClearModelData();
685
686   // The natural size needs to be re-calculated.
687   mImpl->mRecalculateNaturalSize = true;
688
689   // Apply modifications to the model; TODO - Optimize this
690   mImpl->mOperationsPending = ALL_OPERATIONS;
691   mImpl->UpdateModel( ALL_OPERATIONS );
692   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
693                                                            ALIGN              |
694                                                            UPDATE_ACTUAL_SIZE |
695                                                            REORDER );
696
697   // Queue a cursor reposition event; this must wait until after DoRelayout()
698   mImpl->mEventData->mUpdateCursorPosition = true;
699   mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
700 }
701
702 void Controller::TextDeletedEvent()
703 {
704   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected TextDeletedEvent" );
705
706   // TODO - Optimize this
707   ClearModelData();
708
709   // The natural size needs to be re-calculated.
710   mImpl->mRecalculateNaturalSize = true;
711
712   // Apply modifications to the model; TODO - Optimize this
713   mImpl->mOperationsPending = ALL_OPERATIONS;
714   mImpl->UpdateModel( ALL_OPERATIONS );
715   mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT             |
716                                                            ALIGN              |
717                                                            UPDATE_ACTUAL_SIZE |
718                                                            REORDER );
719
720   // Queue a cursor reposition event; this must wait until after DoRelayout()
721   mImpl->mEventData->mUpdateCursorPosition = true;
722   mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
723 }
724
725 bool Controller::DoRelayout( const Size& size,
726                              OperationsMask operationsRequired,
727                              Size& layoutSize )
728 {
729   bool viewUpdated( false );
730
731   // Calculate the operations to be done.
732   const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
733
734   if( LAYOUT & operations )
735   {
736     // Some vectors with data needed to layout and reorder may be void
737     // after the first time the text has been laid out.
738     // Fill the vectors again.
739
740     Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
741
742     if( 0u == numberOfGlyphs )
743     {
744       // Nothing else to do if there is no glyphs.
745       return true;
746     }
747
748     Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
749     Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
750     Vector<CharacterDirection>& characterDirection = mImpl->mLogicalModel->mCharacterDirections;
751     Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
752     Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
753     Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
754
755     // Set the layout parameters.
756     LayoutParameters layoutParameters( size,
757                                        mImpl->mLogicalModel->mText.Begin(),
758                                        lineBreakInfo.Begin(),
759                                        wordBreakInfo.Begin(),
760                                        ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
761                                        numberOfGlyphs,
762                                        glyphs.Begin(),
763                                        glyphsToCharactersMap.Begin(),
764                                        charactersPerGlyph.Begin() );
765
766     // The laid-out lines.
767     // It's not possible to know in how many lines the text is going to be laid-out,
768     // but it can be resized at least with the number of 'paragraphs' to avoid
769     // some re-allocations.
770     Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
771
772     // Delete any previous laid out lines before setting the new ones.
773     lines.Clear();
774
775     // The capacity of the bidirectional paragraph info is the number of paragraphs.
776     lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
777
778     // Resize the vector of positions to have the same size than the vector of glyphs.
779     Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
780     glyphPositions.Resize( numberOfGlyphs );
781
782     // Update the visual model.
783     viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
784                                                    glyphPositions,
785                                                    lines,
786                                                    layoutSize );
787
788     if( viewUpdated )
789     {
790       // Reorder the lines
791       if( REORDER & operations )
792       {
793         Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
794
795         // Check first if there are paragraphs with bidirectional info.
796         if( 0u != bidirectionalInfo.Count() )
797         {
798           // Get the lines
799           const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
800
801           // Reorder the lines.
802           Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
803           lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
804           ReorderLines( bidirectionalInfo,
805                         lines,
806                         lineBidirectionalInfoRuns );
807
808           // Set the bidirectional info into the model.
809           const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
810           mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
811                                                        numberOfBidirectionalInfoRuns );
812
813           // Set the bidirectional info per line into the layout parameters.
814           layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
815           layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
816
817           // Get the character to glyph conversion table and set into the layout.
818           layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
819
820           // Get the glyphs per character table and set into the layout.
821           layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
822
823           // Re-layout the text. Reorder those lines with right to left characters.
824           mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
825                                                          glyphPositions );
826
827           // Free the allocated memory used to store the conversion table in the bidirectional line info run.
828           for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
829                  endIt = lineBidirectionalInfoRuns.End();
830                it != endIt;
831                ++it )
832           {
833             BidirectionalLineInfoRun& bidiLineInfo = *it;
834
835             free( bidiLineInfo.visualToLogicalMap );
836           }
837         }
838       } // REORDER
839
840       if( ALIGN & operations )
841       {
842         mImpl->mLayoutEngine.Align( layoutParameters,
843                                     layoutSize,
844                                     lines,
845                                     glyphPositions );
846       }
847
848       // Sets the actual size.
849       if( UPDATE_ACTUAL_SIZE & operations )
850       {
851         mImpl->mVisualModel->SetActualSize( layoutSize );
852       }
853     } // view updated
854   }
855   else
856   {
857     layoutSize = mImpl->mVisualModel->GetActualSize();
858   }
859
860   return viewUpdated;
861 }
862
863 void Controller::CalculateTextAlignment( const Size& size )
864 {
865   // Get the direction of the first character.
866   const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
867
868   const Size& actualSize = mImpl->mVisualModel->GetActualSize();
869
870   // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
871   LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
872   if( firstParagraphDirection &&
873       ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
874   {
875     if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
876     {
877       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
878     }
879     else
880     {
881       horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
882     }
883   }
884
885   switch( horizontalAlignment )
886   {
887     case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
888     {
889       mImpl->mAlignmentOffset.x = 0.f;
890       break;
891     }
892     case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
893     {
894       const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
895       mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
896       break;
897     }
898     case LayoutEngine::HORIZONTAL_ALIGN_END:
899     {
900       mImpl->mAlignmentOffset.x = size.width - actualSize.width;
901       break;
902     }
903   }
904
905   const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
906   switch( verticalAlignment )
907   {
908     case LayoutEngine::VERTICAL_ALIGN_TOP:
909     {
910       mImpl->mAlignmentOffset.y = 0.f;
911       break;
912     }
913     case LayoutEngine::VERTICAL_ALIGN_CENTER:
914     {
915       const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
916       mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
917       break;
918     }
919     case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
920     {
921       mImpl->mAlignmentOffset.y = size.height - actualSize.height;
922       break;
923     }
924   }
925 }
926
927 LayoutEngine& Controller::GetLayoutEngine()
928 {
929   return mImpl->mLayoutEngine;
930 }
931
932 View& Controller::GetView()
933 {
934   return mImpl->mView;
935 }
936
937 void Controller::KeyboardFocusGainEvent()
938 {
939   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
940
941   if( mImpl->mEventData )
942   {
943     Event event( Event::KEYBOARD_FOCUS_GAIN_EVENT );
944     mImpl->mEventData->mEventQueue.push_back( event );
945
946     mImpl->RequestRelayout();
947   }
948 }
949
950 void Controller::KeyboardFocusLostEvent()
951 {
952   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
953
954   if( mImpl->mEventData )
955   {
956     Event event( Event::KEYBOARD_FOCUS_LOST_EVENT );
957     mImpl->mEventData->mEventQueue.push_back( event );
958
959     mImpl->RequestRelayout();
960   }
961 }
962
963 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
964 {
965   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
966
967   if( mImpl->mEventData &&
968       keyEvent.state == KeyEvent::Down )
969   {
970     int keyCode = keyEvent.keyCode;
971     const std::string& keyString = keyEvent.keyPressed;
972
973     // Pre-process to separate modifying events from non-modifying input events.
974     if( Dali::DALI_KEY_ESCAPE == keyCode )
975     {
976       // Escape key is a special case which causes focus loss
977       KeyboardFocusLostEvent();
978     }
979     else if( Dali::DALI_KEY_CURSOR_LEFT  == keyCode ||
980              Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
981              Dali::DALI_KEY_CURSOR_UP    == keyCode ||
982              Dali::DALI_KEY_CURSOR_DOWN  == keyCode )
983     {
984       Event event( Event::CURSOR_KEY_EVENT );
985       event.p1.mInt = keyCode;
986       mImpl->mEventData->mEventQueue.push_back( event );
987     }
988     else if( Dali::DALI_KEY_BACKSPACE == keyCode )
989     {
990       // Remove the character before the current cursor position
991       bool removed = RemoveText( -1, 1 );
992
993       if( removed )
994       {
995         if( 0u == mImpl->mLogicalModel->mText.Count() )
996         {
997           ShowPlaceholderText();
998         }
999         else
1000         {
1001           mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
1002         }
1003       }
1004     }
1005     else
1006     {
1007       InsertText( keyString, COMMIT );
1008     }
1009
1010     mImpl->ChangeState( EventData::EDITING ); // todo Confirm this is the best place to change the state of
1011
1012     mImpl->RequestRelayout();
1013   }
1014
1015   return false;
1016 }
1017
1018 void Controller::InsertText( const std::string& text, Controller::InsertType type )
1019 {
1020   bool removedPreEdit( false );
1021   bool maxLengthReached( false );
1022
1023   DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
1024   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPreEditFlag %d cursor %d\n",
1025                  this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"), mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPrimaryCursorPosition );
1026
1027   if( ! text.empty() )
1028   {
1029     if( mImpl->IsShowingPlaceholderText() )
1030     {
1031       ResetText();
1032     }
1033   }
1034
1035   if( mImpl->mEventData )
1036   {
1037     if( COMMIT == type )
1038     {
1039       mImpl->mEventData->mPreEditFlag = false;
1040     }
1041     else // PRE_EDIT
1042     {
1043       if( mImpl->mEventData->mPreEditFlag &&
1044           0 != mImpl->mEventData->mPreEditLength )
1045       {
1046         // Remove previous pre-edit text
1047         mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
1048         removedPreEdit = RemoveText( -1, mImpl->mEventData->mPreEditLength );
1049       }
1050       else
1051       {
1052         // Record the start of the pre-edit text
1053         mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
1054         mImpl->mEventData->mPreEditLength = text.size();
1055
1056         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
1057       }
1058
1059       mImpl->mEventData->mPreEditFlag = true;
1060     }
1061   }
1062
1063   if( ! text.empty() )
1064   {
1065     //  Convert text into UTF-32
1066     Vector<Character> utf32Characters;
1067     utf32Characters.Resize( text.size() );
1068
1069     // This is a bit horrible but std::string returns a (signed) char*
1070     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1071
1072     // Transform a text array encoded in utf8 into an array encoded in utf32.
1073     // It returns the actual number of characters.
1074     Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1075     utf32Characters.Resize( characterCount );
1076
1077     DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
1078     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
1079
1080     const Length numberOfCharactersInModel = mImpl->mLogicalModel->GetNumberOfCharacters();
1081
1082     // Restrict new text to fit within Maximum characters setting
1083     Length maxSizeOfNewText = std::min ( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
1084     maxLengthReached = ( characterCount > maxSizeOfNewText );
1085
1086     // Insert at current cursor position
1087     CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
1088
1089     Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1090
1091     if( cursorIndex < numberOfCharactersInModel )
1092     {
1093       modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1094     }
1095     else
1096     {
1097       modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
1098     }
1099
1100     cursorIndex += maxSizeOfNewText;
1101
1102     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
1103   }
1104
1105   if( removedPreEdit || !text.empty() )
1106   {
1107     // Queue an inserted event
1108     mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
1109   }
1110
1111   if( maxLengthReached )
1112   {
1113     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mLogicalModel->mText.Count() );
1114
1115     mImpl->mControlInterface.MaxLengthReached();
1116
1117     mImpl->PreEditReset();
1118   }
1119 }
1120
1121 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1122 {
1123   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
1124
1125   if( mImpl->mEventData )
1126   {
1127     Event event( Event::TAP_EVENT );
1128     event.p1.mUint = tapCount;
1129     event.p2.mFloat = x;
1130     event.p3.mFloat = y;
1131     mImpl->mEventData->mEventQueue.push_back( event );
1132
1133     mImpl->RequestRelayout();
1134   }
1135
1136   // Reset keyboard as tap event has occurred.
1137   mImpl->PreEditReset();
1138 }
1139
1140 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1141 {
1142   DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
1143
1144   if( mImpl->mEventData )
1145   {
1146     Event event( Event::PAN_EVENT );
1147     event.p1.mInt = state;
1148     event.p2.mFloat = displacement.x;
1149     event.p3.mFloat = displacement.y;
1150     mImpl->mEventData->mEventQueue.push_back( event );
1151
1152     mImpl->RequestRelayout();
1153   }
1154 }
1155
1156 void Controller::HandleEvent( HandleType handleType, HandleState state, float x, float y )
1157 {
1158   DALI_ASSERT_DEBUG( mImpl->mEventData && "Controller::HandleEvent. Unexpected HandleEvent" );
1159
1160   if( mImpl->mEventData )
1161   {
1162     switch( handleType )
1163     {
1164       case GRAB_HANDLE:
1165       {
1166         Event event( Event::GRAB_HANDLE_EVENT );
1167         event.p1.mUint  = state;
1168         event.p2.mFloat = x;
1169         event.p3.mFloat = y;
1170
1171         mImpl->mEventData->mEventQueue.push_back( event );
1172         break;
1173       }
1174       case LEFT_SELECTION_HANDLE:
1175       {
1176         Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
1177         event.p1.mUint  = state;
1178         event.p2.mFloat = x;
1179         event.p3.mFloat = y;
1180
1181         mImpl->mEventData->mEventQueue.push_back( event );
1182         break;
1183       }
1184       case RIGHT_SELECTION_HANDLE:
1185       {
1186         Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
1187         event.p1.mUint  = state;
1188         event.p2.mFloat = x;
1189         event.p3.mFloat = y;
1190
1191         mImpl->mEventData->mEventQueue.push_back( event );
1192         break;
1193       }
1194       case HANDLE_TYPE_COUNT:
1195       {
1196         DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
1197       }
1198     }
1199
1200     mImpl->RequestRelayout();
1201   }
1202 }
1203
1204 Controller::~Controller()
1205 {
1206   delete mImpl;
1207 }
1208
1209 void Controller::ShowPlaceholderText()
1210 {
1211   if( mImpl->IsPlaceholderAvailable() )
1212   {
1213     DALI_ASSERT_DEBUG( mImpl->mEventData && "No placeholder text available" );
1214
1215     mImpl->mEventData->mIsShowingPlaceholderText = true;
1216
1217     // Disable handles when showing place-holder text
1218     mImpl->mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1219     mImpl->mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1220     mImpl->mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1221
1222     const char* text( NULL );
1223     size_t size( 0 );
1224
1225     // TODO - Switch placeholder text styles when changing state
1226     if( EventData::INACTIVE != mImpl->mEventData->mState &&
1227         0u != mImpl->mEventData->mPlaceholderTextActive.c_str() )
1228     {
1229       text = mImpl->mEventData->mPlaceholderTextActive.c_str();
1230       size = mImpl->mEventData->mPlaceholderTextActive.size();
1231     }
1232     else
1233     {
1234       text = mImpl->mEventData->mPlaceholderTextInactive.c_str();
1235       size = mImpl->mEventData->mPlaceholderTextInactive.size();
1236     }
1237
1238     // Reset model for showing placeholder.
1239     mImpl->mLogicalModel->mText.Clear();
1240     ClearModelData();
1241     mImpl->mVisualModel->SetTextColor( mImpl->mEventData->mPlaceholderTextColor );
1242
1243     // Convert text into UTF-32
1244     Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1245     utf32Characters.Resize( size );
1246
1247     // This is a bit horrible but std::string returns a (signed) char*
1248     const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text );
1249
1250     // Transform a text array encoded in utf8 into an array encoded in utf32.
1251     // It returns the actual number of characters.
1252     Length characterCount = Utf8ToUtf32( utf8, size, utf32Characters.Begin() );
1253     utf32Characters.Resize( characterCount );
1254
1255     // Reset the cursor position
1256     mImpl->mEventData->mPrimaryCursorPosition = 0;
1257
1258     // The natural size needs to be re-calculated.
1259     mImpl->mRecalculateNaturalSize = true;
1260
1261     // Apply modifications to the model
1262     mImpl->mOperationsPending = ALL_OPERATIONS;
1263     mImpl->QueueModifyEvent( ModifyEvent::TEXT_REPLACED );
1264   }
1265 }
1266
1267 void Controller::ClearModelData()
1268 {
1269   // n.b. This does not Clear the mText from mLogicalModel
1270   mImpl->mLogicalModel->mScriptRuns.Clear();
1271   mImpl->mLogicalModel->mFontRuns.Clear();
1272   mImpl->mLogicalModel->mLineBreakInfo.Clear();
1273   mImpl->mLogicalModel->mWordBreakInfo.Clear();
1274   mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1275   mImpl->mLogicalModel->mCharacterDirections.Clear();
1276   mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1277   mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1278   mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1279   mImpl->mVisualModel->mGlyphs.Clear();
1280   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1281   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1282   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1283   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1284   mImpl->mVisualModel->mGlyphPositions.Clear();
1285   mImpl->mVisualModel->mLines.Clear();
1286   mImpl->mVisualModel->ClearCaches();
1287 }
1288
1289 void Controller::ClearFontData()
1290 {
1291   mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
1292   mImpl->mLogicalModel->mFontRuns.Clear();
1293   mImpl->mVisualModel->mGlyphs.Clear();
1294   mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1295   mImpl->mVisualModel->mCharactersToGlyph.Clear();
1296   mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1297   mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1298   mImpl->mVisualModel->mGlyphPositions.Clear();
1299   mImpl->mVisualModel->mLines.Clear();
1300   mImpl->mVisualModel->ClearCaches();
1301 }
1302
1303 Controller::Controller( ControlInterface& controlInterface )
1304 : mImpl( NULL )
1305 {
1306   mImpl = new Controller::Impl( controlInterface );
1307 }
1308
1309 } // namespace Text
1310
1311 } // namespace Toolkit
1312
1313 } // namespace Dali