Optimizing markup processing
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / text-view / text-view-impl.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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 // CLASS HEADER
18 #include "text-view-impl.h"
19
20 // INTERNAL INCLUDES
21 #include "split-by-new-line-char-policies.h"
22 #include "split-by-word-policies.h"
23 #include "split-by-char-policies.h"
24 #include "text-view-processor.h"
25 #include "text-view-word-processor.h"
26 #include "relayout-utilities.h"
27 #include "text-view-processor-dbg.h"
28
29 namespace Dali
30 {
31
32 namespace Toolkit
33 {
34
35 const Property::Index TextView::PROPERTY_MARKUP_ENABLED( Internal::TextView::TEXTVIEW_PROPERTY_START_INDEX );
36
37 namespace Internal
38 {
39
40 namespace
41 {
42
43 const char* const PROPERTY_TEXT = "text";
44 const char* const PROPERTY_MULTILINE_POLICY = "multiline-policy";
45 const char* const PROPERTY_WIDTH_EXCEED_POLICY = "width-exceed-policy";
46 const char* const PROPERTY_HEIGHT_EXCEED_POLICY = "height-exceed-policy";
47 const char* const PROPERTY_LINE_JUSTIFICATION = "line-justification";
48 const char* const PROPERTY_FADE_BOUNDARY_LEFT = "fade-boundary-left";
49 const char* const PROPERTY_FADE_BOUNDARY_RIGHT = "fade-boundary-right";
50 const char* const PROPERTY_FADE_BOUNDARY_TOP = "fade-boundary-top";
51 const char* const PROPERTY_FADE_BOUNDARY_BOTTOM = "fade-boundary-bottom";
52 const char* const PROPERTY_LINE_HEIGHT_OFFSET = "line-height-offset";
53 const char* const PROPERTY_HORIZONTAL_ALIGNMENT = "horizontal-alignment";
54 const char* const PROPERTY_VERTICAL_ALIGNMENT = "vertical-alignment";
55
56 // Currently on desktop machines 2k x 2k is the maximum frame buffer size, on target is 4k x 4k.
57 const float MAX_OFFSCREEN_RENDERING_SIZE = 2048.f;
58
59 //Type Registration
60 BaseHandle Create()
61 {
62   return Toolkit::TextView::New();
63 }
64
65 TypeRegistration typeRegistration( typeid(Toolkit::TextView), typeid(Toolkit::Control), Create );
66
67 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextView::SIGNAL_TEXT_SCROLLED , &TextView::DoConnectSignal );
68
69 PropertyRegistration property1( typeRegistration, "markup-enabled", Toolkit::TextView::PROPERTY_MARKUP_ENABLED, Property::BOOLEAN, &TextView::SetProperty, &TextView::GetProperty );
70
71 /**
72  * Whether the text-view-processor operation sets, inserts, replaces, removes text.
73  *
74  * @param[in] metadata The text-view-processor operation.
75  *
76  * @return \e true if the given text-view-processor operation is modifying the text.
77  */
78 bool IsTextViewProcessorRelayoutOperation( const TextView::TextViewProcessorMetadata& metadata )
79 {
80   return ( ( metadata.mType == TextView::TextSet ) ||
81            ( metadata.mType == TextView::TextInserted ) ||
82            ( metadata.mType == TextView::TextReplaced ) ||
83            ( metadata.mType == TextView::TextRemoved ) ||
84            ( metadata.mType == TextView::NewStyle ));
85 }
86
87 /**
88  * Whether the text-view-processor operation sets a new line height offset.
89  *
90  * @param[in] metadata The text-view-processor operation.
91  *
92  * @return \e true if the given text-view-processor operation sets a new line height offset.
93  */
94 bool IsTextViewProcessorLineHeightOffsetOperation( const TextView::TextViewProcessorMetadata& metadata )
95 {
96   return ( metadata.mType == TextView::NewLineHeight );
97 }
98
99 /**
100  * Whether the text-view-processor operation sets a new style.
101  *
102  * @param[in] metadata The text-view-processor operation.
103  *
104  * @return \e true if the given text-view-processor operation sets a new style.
105  */
106 bool IsTextViewProcessorNewStyleOperation( const TextView::TextViewProcessorMetadata& metadata )
107 {
108   return ( metadata.mType == TextView::NewStyle );
109 }
110
111 } // namespace
112
113 TextView::TextViewProcessorMetadata::TextViewProcessorMetadata()
114 : mType( TextView::TextSet ),
115   mPosition( 0 ),
116   mNumberOfCharacters( 0 ),
117   mText()
118 {
119 }
120
121 Toolkit::TextView TextView::New()
122 {
123   // Create the implementation, temporarily owned on stack
124   IntrusivePtr<TextView> textView = new TextView();
125
126   // Pass ownership to CustomActor
127   Toolkit::TextView handle( *textView );
128
129   // Second-phase init of the implementation
130   // This can only be done after the CustomActor connection has been made...
131   textView->Initialize();
132
133   // Enables by default the offscreen rendering.
134   textView->SetSnapshotModeEnabled( false ); /// @note Temporary disabled due to some issues with text quality and glyph loading.
135
136   return handle;
137 }
138
139 void TextView::SetText( const std::string& text )
140 {
141   // Creates a styled text with the markup or plain string.
142   MarkupProcessor::StyledTextArray styledText;
143   MarkupProcessor::GetStyledTextArray( text, styledText, IsMarkupProcessingEnabled() );
144
145   // Calls SetText() with the styled text array.
146   SetText( styledText );
147 }
148
149 void TextView::SetText( const MarkupProcessor::StyledTextArray& text )
150 {
151   // mTextViewProcessorOperations stores the InsertTextAt and RemoveTextFrom operations to transform the initial text to mCurrentStyledText.
152   // Once again, if a new text is set, any previous call to InsertTextAt or RemoveTextFrom can be discarted.
153
154   std::vector<TextViewProcessorMetadata>::iterator it = std::remove_if( mTextViewProcessorOperations.begin(), mTextViewProcessorOperations.end(), IsTextViewProcessorRelayoutOperation );
155   mTextViewProcessorOperations.erase( it, mTextViewProcessorOperations.end() );
156
157   // Creates metadata with the Set operation.
158   TextViewProcessorMetadata metadata;
159   metadata.mType = TextView::TextSet;
160   metadata.mText = text;
161
162   // Store metadata.
163   mTextViewProcessorOperations.push_back( metadata );
164
165   // Updates current styled text.
166   mCurrentStyledText = text;
167
168   // Request to be relaid out
169   RelayoutRequest();
170
171   // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values.
172   mRelayoutOperations = RELAYOUT_ALL;
173 }
174
175 void TextView::InsertTextAt( std::size_t position, const std::string& text )
176 {
177   // Creates a styled text with the markup or plain string.
178   MarkupProcessor::StyledTextArray styledText;
179   MarkupProcessor::GetStyledTextArray( text, styledText, IsMarkupProcessingEnabled() );
180
181   // Calls InsertTextAt() with the styled text array.
182   InsertTextAt( position, styledText );
183 }
184
185 void TextView::InsertTextAt( const std::size_t position, const MarkupProcessor::StyledTextArray& text )
186 {
187   // Creates metadata with the Insert operation.
188   TextViewProcessorMetadata metadata;
189   metadata.mType = TextView::TextInserted;
190   metadata.mPosition = position;
191   metadata.mText = text;
192
193   // Store metadata.
194   mTextViewProcessorOperations.push_back( metadata );
195
196   // Updates current styled text.
197   mCurrentStyledText.insert( mCurrentStyledText.begin() + position, text.begin(), text.end() );
198
199   // Request to be relaid out
200   RelayoutRequest();
201
202   // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values.
203   mRelayoutOperations = RELAYOUT_ALL;
204 }
205
206 void TextView::ReplaceTextFromTo( const std::size_t position, const std::size_t numberOfCharacters, const std::string& text )
207 {
208   // Creates a styled text with the markup or plain string.
209   MarkupProcessor::StyledTextArray styledText;
210   MarkupProcessor::GetStyledTextArray( text, styledText, IsMarkupProcessingEnabled() );
211
212   // Calls ReplaceTextFromTo() with the styled text array.
213   ReplaceTextFromTo( position, numberOfCharacters, styledText );
214 }
215
216 void TextView::ReplaceTextFromTo( const std::size_t position, const std::size_t numberOfCharacters, const MarkupProcessor::StyledTextArray& text )
217 {
218   // Creates metadata with the Insert operation.
219   TextViewProcessorMetadata metadata;
220   metadata.mType = TextView::TextReplaced;
221   metadata.mPosition = position;
222   metadata.mNumberOfCharacters = numberOfCharacters;
223   metadata.mText = text;
224
225   // Store metadata.
226   mTextViewProcessorOperations.push_back( metadata );
227
228   // Updates current styled text.
229   MarkupProcessor::StyledTextArray::iterator it = mCurrentStyledText.begin() + position;
230   mCurrentStyledText.erase( it, it + numberOfCharacters );
231   it = mCurrentStyledText.begin() + position;
232   mCurrentStyledText.insert( it, text.begin(), text.end() );
233
234   // Request to be relaid out
235   RelayoutRequest();
236
237   // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values.
238   mRelayoutOperations = RELAYOUT_ALL;
239 }
240
241 void TextView::RemoveTextFrom( const std::size_t position, const std::size_t numberOfCharacters )
242 {
243   // Creates metadata with the Remove operation.
244   TextViewProcessorMetadata metadata;
245   metadata.mType = TextView::TextRemoved;
246   metadata.mPosition = position;
247   metadata.mNumberOfCharacters = numberOfCharacters;
248
249   // Store metadata.
250   mTextViewProcessorOperations.push_back( metadata );
251
252   // Updates current styled text.
253   MarkupProcessor::StyledTextArray::iterator it = mCurrentStyledText.begin() + position;
254   mCurrentStyledText.erase( it, it + numberOfCharacters );
255
256   // Request to be relaid out
257   RelayoutRequest();
258
259   // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values.
260   mRelayoutOperations = RELAYOUT_ALL;
261 }
262
263 std::string TextView::GetText() const
264 {
265   // Traverses the styled text array getting only the text.
266   //  Note that for some languages a 'character' could be represented by more than one 'char'
267
268   std::string text;
269   for( MarkupProcessor::StyledTextArray::const_iterator it = mCurrentStyledText.begin(), endIt = mCurrentStyledText.end(); it != endIt; ++it )
270   {
271     text.append( (*it).mText.GetText() );
272   }
273
274   return text;
275 }
276
277 void TextView::SetLineHeightOffset( const PointSize offset )
278 {
279   if( fabsf( mLayoutParameters.mLineHeightOffset - offset ) > Math::MACHINE_EPSILON_1000 )
280   {
281     // Removes any previous operation which modifies the line height offset.
282     std::vector<TextViewProcessorMetadata>::iterator it = std::remove_if( mTextViewProcessorOperations.begin(), mTextViewProcessorOperations.end(), IsTextViewProcessorLineHeightOffsetOperation );
283     mTextViewProcessorOperations.erase( it, mTextViewProcessorOperations.end() );
284
285     // Creates metadata with the new line height operation.
286     TextViewProcessorMetadata metadata;
287     metadata.mType = TextView::NewLineHeight;
288
289     mTextViewProcessorOperations.push_back( metadata );
290
291     // Updates line height offset.
292     mLayoutParameters.mLineHeightOffset = offset;
293
294     RelayoutRequest();
295
296     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values.
297     if( RELAYOUT_ALL != mRelayoutOperations )
298     {
299       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
300                                                                 RELAYOUT_REMOVE_TEXT_ACTORS |
301                                                                 RELAYOUT_SIZE_POSITION |
302                                                                 RELAYOUT_ALIGNMENT |
303                                                                 RELAYOUT_VISIBILITY |
304                                                                 RELAYOUT_TEXT_ACTOR_UPDATE |
305                                                                 RELAYOUT_INSERT_TO_TEXT_VIEW |
306                                                                 RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
307     }
308   }
309 }
310
311 PointSize TextView::GetLineHeightOffset() const
312 {
313   return PointSize( mLayoutParameters.mLineHeightOffset );
314 }
315
316 void TextView::SetStyleToCurrentText( const TextStyle& style, const TextStyle::Mask mask )
317 {
318   if( !mCurrentStyledText.empty() )
319   {
320     const bool checkFontName = mask & TextStyle::FONT;
321     const bool checkFontSize = mask & TextStyle::SIZE;
322     const bool checkFontStyle = mask & TextStyle::STYLE;
323
324     // Check first if metrics have changed.
325     bool metricsChanged = false;
326     for( MarkupProcessor::StyledTextArray::const_iterator it = mCurrentStyledText.begin(), endIt = mCurrentStyledText.end(); ( it != endIt ) && !metricsChanged; ++it )
327     {
328       const MarkupProcessor::StyledText& styledText( *it );
329
330       metricsChanged = ( checkFontName && ( styledText.mStyle.GetFontName() != style.GetFontName() ) ) ||
331                        ( checkFontStyle && ( styledText.mStyle.GetFontStyle() != style.GetFontStyle() ) ) ||
332                        ( checkFontSize && ( fabsf( styledText.mStyle.GetFontPointSize() - style.GetFontPointSize() ) > Math::MACHINE_EPSILON_1000 ) );
333     }
334
335     if( metricsChanged )
336     {
337       MarkupProcessor::SetTextStyle( mCurrentStyledText, style, mask );
338
339       // If metrics change, new text measurements are needed.
340       SetText( mCurrentStyledText );
341     }
342     else
343     {
344       // Deletes any previous operation which sets a new style.
345       std::vector<TextViewProcessorMetadata>::iterator it = std::remove_if( mTextViewProcessorOperations.begin(), mTextViewProcessorOperations.end(), IsTextViewProcessorNewStyleOperation );
346       mTextViewProcessorOperations.erase( it, mTextViewProcessorOperations.end() );
347
348       // Creates metadata with the new style operation.
349       TextViewProcessorMetadata metadata;
350       metadata.mType = TextView::NewStyle;
351
352       MarkupProcessor::StyledText text;
353       text.mStyle = style;
354       metadata.mText.push_back( text );
355       metadata.mStyleMask = mask;
356
357       mTextViewProcessorOperations.push_back( metadata );
358
359       MarkupProcessor::SetTextStyle( mCurrentStyledText, style, mask );
360
361       RelayoutRequest();
362
363       if( RELAYOUT_ALL != mRelayoutOperations )
364       {
365         mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
366                                                                   RELAYOUT_TEXT_ACTOR_UPDATE );
367       }
368     }
369   }
370
371   // Sets the new style to the ellipsize text
372   if( !mLayoutParameters.mEllipsizeText.empty() )
373   {
374     for( MarkupProcessor::StyledTextArray::iterator it = mLayoutParameters.mEllipsizeText.begin(), endIt = mLayoutParameters.mEllipsizeText.end(); it != endIt; ++it )
375     {
376       (*it).mStyle.Copy( style, mask );
377     }
378
379     SetEllipsizeText( mLayoutParameters.mEllipsizeText );
380   }
381 }
382
383 void TextView::SetTextAlignment( Toolkit::Alignment::Type align )
384 {
385   if( align != ( mLayoutParameters.mHorizontalAlignment | mLayoutParameters.mVerticalAlignment ) )
386   {
387     Toolkit::Alignment::Type horizontalAlignment( ( align & Toolkit::Alignment::HorizontalLeft ? Toolkit::Alignment::HorizontalLeft :
388                                                     ( align & Toolkit::Alignment::HorizontalCenter ? Toolkit::Alignment::HorizontalCenter :
389                                                       ( align & Toolkit::Alignment::HorizontalRight ? Toolkit::Alignment::HorizontalRight : Toolkit::Alignment::HorizontalCenter ) ) ) );
390     Toolkit::Alignment::Type verticalAlignment( ( align & Toolkit::Alignment::VerticalTop ? Toolkit::Alignment::VerticalTop :
391                                                   ( align & Toolkit::Alignment::VerticalCenter ? Toolkit::Alignment::VerticalCenter :
392                                                     ( align & Toolkit::Alignment::VerticalBottom ? Toolkit::Alignment::VerticalBottom : Toolkit::Alignment::VerticalCenter ) ) ) );
393
394     mLayoutParameters.mHorizontalAlignment = horizontalAlignment;
395     mLayoutParameters.mVerticalAlignment = verticalAlignment;
396
397     RelayoutRequest();
398
399     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values.
400     if( RELAYOUT_ALL != mRelayoutOperations )
401     {
402       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
403                                                                 RELAYOUT_TEXT_ACTOR_UPDATE |
404                                                                 RELAYOUT_ALIGNMENT |
405                                                                 RELAYOUT_VISIBILITY );
406     }
407   }
408 }
409
410 Toolkit::Alignment::Type TextView::GetTextAlignment() const
411 {
412   return static_cast<Toolkit::Alignment::Type>( mLayoutParameters.mHorizontalAlignment | mLayoutParameters.mVerticalAlignment );
413 }
414
415 void TextView::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
416 {
417   if( policy != mLayoutParameters.mMultilinePolicy )
418   {
419     mLayoutParameters.mMultilinePolicy = policy;
420
421     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values.
422     mRelayoutOperations = RELAYOUT_ALL;
423
424     RelayoutRequest();
425   }
426 }
427
428 Toolkit::TextView::MultilinePolicy TextView::GetMultilinePolicy() const
429 {
430   return mLayoutParameters.mMultilinePolicy;
431 }
432
433 void TextView::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
434 {
435   // The layout info could be invalid depending on the current exceed policy and the new one.
436   // i.e. if the current policy is Split and the new one is ShrinkToFit then
437   // the layout info generated for each char is not needed.
438   if( policy != mLayoutParameters.mWidthExceedPolicy )
439   {
440     mLayoutParameters.mWidthExceedPolicy = policy;
441
442     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values.
443     mRelayoutOperations = RELAYOUT_ALL;
444
445     RelayoutRequest();
446   }
447 }
448
449 Toolkit::TextView::ExceedPolicy TextView::GetWidthExceedPolicy() const
450 {
451   return mLayoutParameters.mWidthExceedPolicy;
452 }
453
454 void TextView::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
455 {
456   if( policy != mLayoutParameters.mHeightExceedPolicy )
457   {
458     mLayoutParameters.mHeightExceedPolicy = policy;
459
460     RelayoutRequest();
461
462     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values.
463     if( RELAYOUT_ALL != mRelayoutOperations )
464     {
465       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
466                                                                 RELAYOUT_REMOVE_TEXT_ACTORS |
467                                                                 RELAYOUT_SIZE_POSITION |
468                                                                 RELAYOUT_ALIGNMENT |
469                                                                 RELAYOUT_VISIBILITY |
470                                                                 RELAYOUT_TEXT_ACTOR_UPDATE |
471                                                                 RELAYOUT_INSERT_TO_TEXT_VIEW |
472                                                                 RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
473     }
474   }
475 }
476
477 Toolkit::TextView::ExceedPolicy TextView::GetHeightExceedPolicy() const
478 {
479   return mLayoutParameters.mHeightExceedPolicy;
480 }
481
482 void TextView::SetLineJustification( Toolkit::TextView::LineJustification justification )
483 {
484   if( justification != mLayoutParameters.mLineJustification )
485   {
486     mLayoutParameters.mLineJustification = justification;
487
488     RelayoutRequest();
489
490     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values.
491     if( RELAYOUT_ALL != mRelayoutOperations )
492     {
493       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
494                                                                 RELAYOUT_REMOVE_TEXT_ACTORS |
495                                                                 RELAYOUT_SIZE_POSITION |
496                                                                 RELAYOUT_ALIGNMENT |
497                                                                 RELAYOUT_VISIBILITY |
498                                                                 RELAYOUT_TEXT_ACTOR_UPDATE |
499                                                                 RELAYOUT_INSERT_TO_TEXT_VIEW |
500                                                                 RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
501     }
502   }
503 }
504
505 Toolkit::TextView::LineJustification TextView::GetLineJustification() const
506 {
507   return mLayoutParameters.mLineJustification;
508 }
509
510 void TextView::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
511 {
512   if( ( fadeBoundary.mLeft != mVisualParameters.mFadeBoundary.mLeft )   ||
513       ( fadeBoundary.mRight != mVisualParameters.mFadeBoundary.mRight ) ||
514       ( fadeBoundary.mTop != mVisualParameters.mFadeBoundary.mTop )     ||
515       ( fadeBoundary.mBottom != mVisualParameters.mFadeBoundary.mBottom ) )
516   {
517     mVisualParameters.mFadeBoundary = fadeBoundary;
518
519     RelayoutRequest();
520
521     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values.
522     if( RELAYOUT_ALL != mRelayoutOperations )
523     {
524       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
525                                                                 RELAYOUT_REMOVE_TEXT_ACTORS |
526                                                                 RELAYOUT_VISIBILITY |
527                                                                 RELAYOUT_TEXT_ACTOR_UPDATE |
528                                                                 RELAYOUT_INSERT_TO_TEXT_VIEW |
529                                                                 RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
530     }
531   }
532 }
533
534 const Toolkit::TextView::FadeBoundary& TextView::GetFadeBoundary() const
535 {
536   return mVisualParameters.mFadeBoundary;
537 }
538
539 void TextView::SetEllipsizeText( const std::string& ellipsizeText )
540 {
541   // Creates a styled text with the markup or plain string.
542   MarkupProcessor::StyledTextArray styledText;
543   MarkupProcessor::GetStyledTextArray( ellipsizeText, styledText, IsMarkupProcessingEnabled() );
544
545   SetEllipsizeText( styledText );
546 }
547
548 void TextView::SetEllipsizeText( const MarkupProcessor::StyledTextArray& ellipsizeText )
549 {
550   mLayoutParameters.mEllipsizeText = ellipsizeText;
551
552   mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo = TextViewProcessor::WordLayoutInfo();
553
554   TextViewProcessor::CreateWordTextInfo( mLayoutParameters.mEllipsizeText,
555                                          mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo );
556
557   // Request to be relaid out
558   RelayoutRequest();
559
560   mRelayoutOperations = RELAYOUT_ALL;
561 }
562
563 std::string TextView::GetEllipsizeText() const
564 {
565   std::string text;
566   for( MarkupProcessor::StyledTextArray::const_iterator it = mLayoutParameters.mEllipsizeText.begin(), endIt = mLayoutParameters.mEllipsizeText.end(); it != endIt; ++it )
567   {
568     text.append( (*it).mText.GetText() );
569   }
570
571   return text;
572 }
573
574 void TextView::GetTextLayoutInfo()
575 {
576   const bool relayoutSizeAndPositionNeeded = mRelayoutOperations & RELAYOUT_SIZE_POSITION;
577   const bool relayoutAlignmentNeeded = mRelayoutOperations & RELAYOUT_ALIGNMENT;
578   const bool relayoutVisibilityNeeded = mRelayoutOperations & RELAYOUT_VISIBILITY;
579
580   if( relayoutSizeAndPositionNeeded || relayoutAlignmentNeeded || relayoutVisibilityNeeded )
581   {
582     Vector3 textViewSize = GetControlSize();
583
584     if( ( ( textViewSize.width < Math::MACHINE_EPSILON_1000 ) ||
585           ( textViewSize.height < Math::MACHINE_EPSILON_1000 ) ) &&
586         ( ( Toolkit::TextView::SplitByNewLineChar == mLayoutParameters.mMultilinePolicy ) &&
587           ( Toolkit::TextView::Original == mLayoutParameters.mWidthExceedPolicy ) &&
588           ( Toolkit::TextView::Original == mLayoutParameters.mHeightExceedPolicy ) ) )
589     {
590       // In case the control size is not set but the layout settings are the default (split by new line character and original exceed policies)
591       // the text natural size can be used.
592       textViewSize = GetNaturalSize();
593     }
594
595     if( ( textViewSize.width > Math::MACHINE_EPSILON_1000 ) &&
596         ( textViewSize.height > Math::MACHINE_EPSILON_1000 ) )
597     {
598       // Check if the text-view has glyph-actors.
599       const bool hasGlyphActors = !mRelayoutData.mGlyphActors.empty();
600
601       RelayoutOperationMask mask = NO_RELAYOUT;
602       if( relayoutSizeAndPositionNeeded )
603       {
604         mask = static_cast<RelayoutOperationMask>( mask | RELAYOUT_SIZE_POSITION );
605       }
606       if( relayoutAlignmentNeeded )
607       {
608         mask = static_cast<RelayoutOperationMask>( mask | RELAYOUT_ALIGNMENT );
609       }
610       if( relayoutVisibilityNeeded )
611       {
612         mask = static_cast<RelayoutOperationMask>( mask | RELAYOUT_VISIBILITY );
613       }
614
615       if( hasGlyphActors )
616       {
617         // Remove glyph-actors from the text-view as some text-operation like CreateTextInfo()
618         // add them to the text-actor cache.
619         TextViewRelayout::RemoveGlyphActors( GetRootActor(), mRelayoutData.mGlyphActors );
620         mRelayoutData.mGlyphActors.clear();
621       }
622
623       // Relays-out but doesn't add glyph-actors to the text-view.
624       DoRelayOut( textViewSize.GetVectorXY(), mask );
625
626       if( hasGlyphActors )
627       {
628         mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_VIEW );
629         mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
630       }
631
632     }
633   }
634 }
635
636 void TextView::GetTextLayoutInfo( Toolkit::TextView::TextLayoutInfo& textLayoutInfo )
637 {
638   GetTextLayoutInfo();
639
640   textLayoutInfo.mCharacterLayoutInfoTable = mRelayoutData.mCharacterLayoutInfoTable;
641   textLayoutInfo.mLines = mRelayoutData.mLines;
642
643   textLayoutInfo.mCharacterLogicalToVisualMap = mRelayoutData.mCharacterLogicalToVisualMap;
644   textLayoutInfo.mCharacterVisualToLogicalMap = mRelayoutData.mCharacterVisualToLogicalMap;
645
646   textLayoutInfo.mTextSize = mRelayoutData.mTextSizeForRelayoutOption;
647
648   textLayoutInfo.mScrollOffset = mVisualParameters.mCameraScrollPosition;
649 }
650
651 void TextView::SetSortModifier( float depthOffset )
652 {
653   mVisualParameters.mSortModifier = depthOffset;
654
655   for( std::vector<RenderableActor>::iterator it = mRelayoutData.mGlyphActors.begin(), endIt = mRelayoutData.mGlyphActors.end();
656        it != endIt;
657        ++it )
658   {
659     ( *it ).SetSortModifier( depthOffset );
660   }
661
662   if( mOffscreenImageActor )
663   {
664     mOffscreenImageActor.SetSortModifier( depthOffset );
665   }
666 }
667
668 void TextView::SetSnapshotModeEnabled( bool enable )
669 {
670   if( enable != mVisualParameters.mSnapshotModeEnabled )
671   {
672     // Remove first all glyph-actors
673     if( !mRelayoutData.mGlyphActors.empty() )
674     {
675       TextViewRelayout::RemoveGlyphActors( GetRootActor(), mRelayoutData.mGlyphActors );
676     }
677
678     mVisualParameters.mSnapshotModeEnabled = enable;
679     if( !mLockPreviousSnapshotMode )
680     {
681       // mPreviousSnapshotModeEnabled stores the snapshot mode value before SetScrollEnabled( true ) is
682       // called. However, if SetSnapshotModeEnabled() is called after SetScrollEnabled() then the stored value
683       // is updated.
684       // As SetSnapshotModeEnabled() is also called from SetScrollEnabled(), the mLockPreviousSnapshotMode prevents
685       // to smash the stored value.
686       mPreviousSnapshotModeEnabled = enable;
687     }
688
689     if( mVisualParameters.mSnapshotModeEnabled )
690     {
691       // Create a root actor and an image actor for offscreen rendering.
692       mOffscreenRootActor = Layer::New();
693       mOffscreenImageActor = ImageActor::New();
694
695       mOffscreenRootActor.SetColorMode( USE_OWN_COLOR );
696       mOffscreenRootActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
697       mOffscreenRootActor.SetInheritRotation( false );
698       mOffscreenRootActor.SetInheritScale( false );
699       mOffscreenRootActor.SetDepthTestDisabled( true );
700
701       mOffscreenRootActor.SetPosition( 0.f, 0.f, 0.f );
702
703       mOffscreenImageActor.SetAnchorPoint( ParentOrigin::CENTER );
704       mOffscreenImageActor.SetParentOrigin( ParentOrigin::CENTER );
705
706       Actor self = Self();
707       self.Add( mOffscreenRootActor );
708       self.Add( mOffscreenImageActor );
709       mOffscreenImageActor.SetScale(Vector3(1.f, -1.f, 1.f));
710     }
711     else
712     {
713       Actor self = Self();
714
715       if( mOffscreenRootActor )
716       {
717         self.Remove( mOffscreenRootActor );
718       }
719
720       if( mOffscreenImageActor )
721       {
722         self.Remove( mOffscreenImageActor );
723       }
724
725       DestroyOffscreenRenderingResources();
726     }
727
728     if( RELAYOUT_ALL != mRelayoutOperations )
729     {
730       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
731                                                                 RELAYOUT_REMOVE_TEXT_ACTORS |
732                                                                 RELAYOUT_TEXT_ACTOR_UPDATE |
733                                                                 RELAYOUT_INSERT_TO_TEXT_VIEW |
734                                                                 RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
735     }
736     RelayoutRequest();
737   }
738 }
739
740 bool TextView::IsSnapshotModeEnabled() const
741 {
742   return mVisualParameters.mSnapshotModeEnabled;
743 }
744
745 void TextView::SetMarkupProcessingEnabled( bool enable )
746 {
747   mMarkUpEnabled = enable;
748 }
749
750 bool TextView::IsMarkupProcessingEnabled() const
751 {
752   return mMarkUpEnabled;
753 }
754
755 void TextView::SetScrollEnabled( const bool enable )
756 {
757   if( enable != mVisualParameters.mScrollEnabled )
758   {
759     mVisualParameters.mScrollEnabled = enable;
760
761     if( mVisualParameters.mScrollEnabled )
762     {
763       // Offscreen rendering is needed to enable text scroll.
764
765       // Stores previous value of the snapshot mode.
766       mPreviousSnapshotModeEnabled = IsSnapshotModeEnabled();
767
768       {
769         // SetSnapshotModeEnabled() modifies the mPreviousSnapshotModeEnabled just in case it's called after SetScrollEnabled(),
770         // this lock prevents to modify the mPreviousSnapshotModeEnabled when SetSnapshotModeEnabled() from this method.
771         Lock lock( mLockPreviousSnapshotMode );
772         SetSnapshotModeEnabled( true );
773       }
774
775       // Creates the pan gesture detector and attach the text-view.
776       mPanGestureDetector = PanGestureDetector::New();
777       mPanGestureDetector.DetectedSignal().Connect( this, &TextView::OnTextPan );
778       mPanGestureDetector.Attach( Self() );
779     }
780     else
781     {
782       // Removes the pan gesture detector.
783       if( mPanGestureDetector )
784       {
785         mPanGestureDetector.Detach( Self() );
786         mPanGestureDetector.DetectedSignal().Disconnect( this, &TextView::OnTextPan );
787         mPanGestureDetector.Reset();
788       }
789
790       // Restores the previous state for snapshot mode.
791       SetSnapshotModeEnabled( mPreviousSnapshotModeEnabled );
792     }
793   }
794 }
795
796 bool TextView::IsScrollEnabled() const
797 {
798   return mVisualParameters.mScrollEnabled;
799 }
800
801 void TextView::SetScrollPosition( const Vector2& position )
802 {
803   if( position != mVisualParameters.mCameraScrollPosition )
804   {
805     // Guard against destruction during signal emission
806     // Note that Emit() methods are called indirectly from within DoSetScrollPosition()
807     Toolkit::TextView handle( GetOwner() );
808
809     DoSetScrollPosition( position );
810
811     // Check if the new scroll position has been trimmed.
812     mVisualParameters.mScrollPositionTrimmed = ( position != mVisualParameters.mCameraScrollPosition );
813   }
814 }
815
816 const Vector2& TextView::GetScrollPosition() const
817 {
818   return mVisualParameters.mCameraScrollPosition;
819 }
820
821 bool TextView::IsScrollPositionTrimmed() const
822 {
823   return mVisualParameters.mScrollPositionTrimmed;
824 }
825
826 Toolkit::TextView::ScrolledSignalV2& TextView::ScrolledSignal()
827 {
828   return mScrolledSignalV2;
829 }
830
831 bool TextView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
832 {
833   Dali::BaseHandle handle( object );
834
835   bool connected( true );
836   Toolkit::TextView textView = Toolkit::TextView::DownCast(handle);
837
838   if( Dali::Toolkit::TextView::SIGNAL_TEXT_SCROLLED == signalName )
839   {
840     textView.ScrolledSignal().Connect( tracker, functor );
841   }
842   else
843   {
844     // signalName does not match any signal
845     connected = false;
846   }
847
848   return connected;
849 }
850
851 TextView::LayoutParameters::LayoutParameters()
852 : mMultilinePolicy( Toolkit::TextView::SplitByNewLineChar ),
853   mWidthExceedPolicy( Toolkit::TextView::Original ),
854   mHeightExceedPolicy( Toolkit::TextView::Original ),
855   mHorizontalAlignment( Toolkit::Alignment::HorizontalCenter ),
856   mVerticalAlignment( Toolkit::Alignment::VerticalCenter ),
857   mLineJustification( Toolkit::TextView::Left ),
858   mLineHeightOffset( 0.f ),
859   mEllipsizeText(),
860   mMarkUpEnabled( false )
861 {
862   // Sets ellipsize text
863   MarkupProcessor::StyledTextArray styledEllipsize;
864   MarkupProcessor::GetStyledTextArray( std::string( "..." ), mEllipsizeText, false );
865 }
866
867 TextView::LayoutParameters::LayoutParameters( const Toolkit::TextView::MultilinePolicy   multilinePolicy,
868                                               const Toolkit::TextView::ExceedPolicy      widthExceedPolicy,
869                                               const Toolkit::TextView::ExceedPolicy      heightExceedPolicy,
870                                               const Toolkit::Alignment::Type             alignmentType,
871                                               const Toolkit::TextView::LineJustification lineJustification,
872                                               const float                                lineHeightOffset,
873                                               const std::string&                         ellipsizeText,
874                                               const bool                                 markUpEnabled )
875 : mMultilinePolicy( multilinePolicy ),
876   mWidthExceedPolicy( widthExceedPolicy ),
877   mHeightExceedPolicy( heightExceedPolicy ),
878   mHorizontalAlignment(),
879   mVerticalAlignment(),
880   mLineJustification( lineJustification ),
881   mLineHeightOffset( lineHeightOffset ),
882   mEllipsizeText(),
883   mMarkUpEnabled( markUpEnabled )
884 {
885   // Sets alignment
886   Toolkit::Alignment::Type horizontalAlignment( ( alignmentType & Toolkit::Alignment::HorizontalLeft ? Toolkit::Alignment::HorizontalLeft :
887                                                   ( alignmentType & Toolkit::Alignment::HorizontalCenter ? Toolkit::Alignment::HorizontalCenter :
888                                                     ( alignmentType & Toolkit::Alignment::HorizontalRight ? Toolkit::Alignment::HorizontalRight : Toolkit::Alignment::HorizontalCenter ) ) ) );
889   Toolkit::Alignment::Type verticalAlignment( ( alignmentType & Toolkit::Alignment::VerticalTop ? Toolkit::Alignment::VerticalTop :
890                                                 ( alignmentType & Toolkit::Alignment::VerticalCenter ? Toolkit::Alignment::VerticalCenter :
891                                                   ( alignmentType & Toolkit::Alignment::VerticalBottom ? Toolkit::Alignment::VerticalBottom : Toolkit::Alignment::VerticalCenter ) ) ) );
892
893   mHorizontalAlignment = horizontalAlignment;
894   mVerticalAlignment = verticalAlignment;
895
896   // Sets ellipsize text
897   MarkupProcessor::StyledTextArray styledEllipsize;
898   MarkupProcessor::GetStyledTextArray( ellipsizeText, mEllipsizeText, mMarkUpEnabled );
899 }
900
901 TextView::LayoutParameters::LayoutParameters( const TextView::LayoutParameters& layoutParameters )
902 : mMultilinePolicy( layoutParameters.mMultilinePolicy ),
903   mWidthExceedPolicy( layoutParameters.mWidthExceedPolicy ),
904   mHeightExceedPolicy( layoutParameters.mHeightExceedPolicy ),
905   mHorizontalAlignment( layoutParameters.mHorizontalAlignment ),
906   mVerticalAlignment( layoutParameters.mVerticalAlignment ),
907   mLineJustification( layoutParameters.mLineJustification ),
908   mLineHeightOffset( layoutParameters.mLineHeightOffset ),
909   mEllipsizeText( layoutParameters.mEllipsizeText ),
910   mMarkUpEnabled( layoutParameters.mMarkUpEnabled )
911 {
912 }
913
914 TextView::LayoutParameters& TextView::LayoutParameters::operator=( const TextView::LayoutParameters& layoutParameters )
915 {
916   mMultilinePolicy = layoutParameters.mMultilinePolicy;
917   mWidthExceedPolicy = layoutParameters.mWidthExceedPolicy;
918   mHeightExceedPolicy = layoutParameters.mHeightExceedPolicy;
919   mHorizontalAlignment = layoutParameters.mHorizontalAlignment;
920   mVerticalAlignment = layoutParameters.mVerticalAlignment;
921   mLineJustification = layoutParameters.mLineJustification;
922   mLineHeightOffset = layoutParameters.mLineHeightOffset;
923   mEllipsizeText = layoutParameters.mEllipsizeText;
924   mMarkUpEnabled = layoutParameters.mMarkUpEnabled;
925
926   return *this;
927 }
928
929 TextView::VisualParameters::VisualParameters()
930 : mFadeBoundary(),
931   mSortModifier( 0.f ),
932   mCameraScrollPosition( 0.f, 0.f ),
933   mSnapshotModeEnabled( false ),
934   mScrollEnabled( false ),
935   mScrollPositionTrimmed( false )
936 {
937 }
938
939 TextView::VisualParameters::VisualParameters( const VisualParameters& visualParameters )
940 : mFadeBoundary( visualParameters.mFadeBoundary ),
941   mSortModifier( visualParameters.mSortModifier ),
942   mCameraScrollPosition( visualParameters.mCameraScrollPosition ),
943   mSnapshotModeEnabled( visualParameters.mSnapshotModeEnabled ),
944   mScrollEnabled( visualParameters.mScrollEnabled ),
945   mScrollPositionTrimmed( visualParameters.mScrollPositionTrimmed )
946 {
947 }
948
949 TextView::VisualParameters& TextView::VisualParameters::operator=( const TextView::VisualParameters& visualParameters )
950 {
951   mFadeBoundary = visualParameters.mFadeBoundary;
952   mSortModifier = visualParameters.mSortModifier;
953   mCameraScrollPosition = visualParameters.mCameraScrollPosition;
954   mSnapshotModeEnabled = visualParameters.mSnapshotModeEnabled;
955   mScrollEnabled = visualParameters.mScrollEnabled;
956   mScrollPositionTrimmed = visualParameters.mScrollPositionTrimmed;
957
958   return *this;
959 }
960
961 TextView::RelayoutData::RelayoutData()
962 : mTextViewSize(),
963   mShrinkFactor( 1.f ),
964   mTextLayoutInfo(),
965   mCharacterLogicalToVisualMap(),
966   mCharacterVisualToLogicalMap(),
967   mGlyphActors(),
968   mCharacterLayoutInfoTable(),
969   mLines(),
970   mTextSizeForRelayoutOption()
971 {
972 }
973
974 TextView::RelayoutData::RelayoutData( const TextView::RelayoutData& relayoutData )
975 : mTextViewSize( relayoutData.mTextViewSize ),
976   mShrinkFactor( relayoutData.mShrinkFactor ),
977   mTextLayoutInfo( relayoutData.mTextLayoutInfo ),
978   mCharacterLogicalToVisualMap( relayoutData.mCharacterLogicalToVisualMap ),
979   mCharacterVisualToLogicalMap( relayoutData.mCharacterVisualToLogicalMap ),
980   mGlyphActors( relayoutData.mGlyphActors ),
981   mCharacterLayoutInfoTable( relayoutData.mCharacterLayoutInfoTable ),
982   mLines( relayoutData.mLines ),
983   mTextSizeForRelayoutOption( relayoutData.mTextSizeForRelayoutOption )
984 {
985 }
986
987 TextView::RelayoutData& TextView::RelayoutData::operator=( const TextView::RelayoutData& relayoutData )
988 {
989   mTextViewSize = relayoutData.mTextViewSize;
990   mShrinkFactor = relayoutData.mShrinkFactor;
991   mTextLayoutInfo = relayoutData.mTextLayoutInfo;
992   mCharacterLogicalToVisualMap = relayoutData.mCharacterLogicalToVisualMap;
993   mCharacterVisualToLogicalMap = relayoutData.mCharacterVisualToLogicalMap;
994   mGlyphActors = relayoutData.mGlyphActors;
995   mCharacterLayoutInfoTable = relayoutData.mCharacterLayoutInfoTable;
996   mLines = relayoutData.mLines;
997   mTextSizeForRelayoutOption = relayoutData.mTextSizeForRelayoutOption;
998
999   return *this;
1000 }
1001
1002 TextView::TextView()
1003 : ControlImpl( false ),  // doesn't require touch events
1004   mCurrentStyledText(),
1005   mTextViewProcessorOperations(),
1006   mLayoutParameters( Toolkit::TextView::SplitByNewLineChar,
1007                      Toolkit::TextView::Original,
1008                      Toolkit::TextView::Original,
1009                      static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalCenter | Toolkit::Alignment::VerticalCenter ),
1010                      Toolkit::TextView::Left,
1011                      PointSize( 0.f ),
1012                      std::string( "..." ),
1013                      false ),
1014   mVisualParameters(),
1015   mRelayoutData(),
1016   mRelayoutOperations( NO_RELAYOUT ),
1017   mOffscreenRootActor(),
1018   mOffscreenImageActor(),
1019   mOffscreenCameraActor(),
1020   mCurrentOffscreenSize(),
1021   mFrameBufferImage(),
1022   mRenderTask(),
1023   mPanGestureDetector(),
1024   mLockPreviousSnapshotMode( false ),
1025   mPreviousSnapshotModeEnabled( false ),
1026   mMarkUpEnabled( false )
1027 {
1028   TextViewProcessor::CreateWordTextInfo( mLayoutParameters.mEllipsizeText,
1029                                          mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo );
1030 }
1031
1032 TextView::~TextView()
1033 {
1034   // Destroys offscreen rendering resources.
1035   DestroyOffscreenRenderingResources();
1036
1037   // Destroys scroll pan gesture detector.
1038   if( mPanGestureDetector )
1039   {
1040     mPanGestureDetector.Reset();
1041   }
1042 }
1043
1044 Vector3 TextView::GetNaturalSize()
1045 {
1046   if( !mTextViewProcessorOperations.empty() )
1047   {
1048     // There are SetText, Inserts or Removes to do. It means the current layout info is not updated.
1049
1050     if( !mRelayoutData.mGlyphActors.empty() )
1051     {
1052       // Remove glyph-actors from the text-view as some text-operation like CreateTextInfo()
1053       // add them to the text-actor cache.
1054       TextViewRelayout::RemoveGlyphActors( GetRootActor(), mRelayoutData.mGlyphActors );
1055       mRelayoutData.mGlyphActors.clear();
1056
1057       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_VIEW );
1058       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
1059     }
1060
1061     PerformTextViewProcessorOperations();
1062   }
1063
1064   return Vector3( mRelayoutData.mTextLayoutInfo.mWholeTextSize.width, mRelayoutData.mTextLayoutInfo.mWholeTextSize.height, 0.f );
1065 }
1066
1067 float TextView::GetHeightForWidth( float width )
1068 {
1069   float height = 0.f;
1070
1071   if( ( Toolkit::TextView::SplitByNewLineChar == mLayoutParameters.mMultilinePolicy ) &&
1072       ( Toolkit::TextView::Original == mLayoutParameters.mWidthExceedPolicy ) &&
1073       ( Toolkit::TextView::Original == mLayoutParameters.mHeightExceedPolicy ) )
1074   {
1075     // If multiline and exceed policies are 'SplitByNewLineChar' and 'Original' is better get the height from the
1076     // natural size. GetNaturalSize() for this configuration is faster than DoRelayOut().
1077     height = GetNaturalSize().height;
1078   }
1079   else
1080   {
1081     // Check if the given width is different than the current one.
1082     const bool differentWidth = ( fabsf( width - mRelayoutData.mTextViewSize.width ) > Math::MACHINE_EPSILON_1000 );
1083
1084     // Check if the text-view has glyph-actors.
1085     const bool hasGlyphActors = !mRelayoutData.mGlyphActors.empty();
1086
1087     // Check which layout operations need to be done.
1088     const bool relayoutSizeAndPositionNeeded = ( mRelayoutOperations & RELAYOUT_SIZE_POSITION ) || differentWidth;
1089
1090     if( relayoutSizeAndPositionNeeded )
1091     {
1092       if( hasGlyphActors )
1093       {
1094         // Remove glyph-actors from the text-view as some text-operation like CreateTextInfo()
1095         // add them to the text-actor cache.
1096         TextViewRelayout::RemoveGlyphActors( GetRootActor(), mRelayoutData.mGlyphActors );
1097         mRelayoutData.mGlyphActors.clear();
1098       }
1099
1100       // Use the given width.
1101       const Vector2 textViewSize( width, GetControlSize().height );
1102
1103       // Relays-out but doesn't add glyph-actors to the text-view.
1104       DoRelayOut( textViewSize, RELAYOUT_SIZE_POSITION );
1105     }
1106
1107     // Retrieve the text height after relayout the text.
1108     height = mRelayoutData.mTextSizeForRelayoutOption.height;
1109
1110     if( differentWidth )
1111     {
1112       // Revert the relayout operation mask
1113       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_SIZE_POSITION );
1114     }
1115
1116     if( hasGlyphActors )
1117     {
1118       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_VIEW );
1119       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
1120     }
1121
1122     if( differentWidth || hasGlyphActors )
1123     {
1124       RelayoutRequest();
1125     }
1126   }
1127
1128   return height;
1129 }
1130
1131 float TextView::GetWidthForHeight( float height )
1132 {
1133   // TODO: Needs implementing properly, for now just return the natural width.
1134   return GetNaturalSize().width;
1135 }
1136
1137 void TextView::OnPropertySet( Property::Index index, Property::Value propertyValue )
1138 {
1139   if( index == mPropertyText )
1140   {
1141     SetText(propertyValue.Get<std::string>());
1142   }
1143   else if( index == mPropertyMultilinePolicy )
1144   {
1145     OnMultilinePolicyPropertySet(propertyValue);
1146   }
1147   else if( index == mPropertyWidthExceedPolicy )
1148   {
1149     OnWidthExceedPolicyPropertySet(propertyValue);
1150   }
1151   else if( index == mPropertyHeightExceedPolicy )
1152   {
1153     OnHeightExceedPolicyPropertySet(propertyValue);
1154   }
1155   else if( index == mPropertyLineJustification )
1156   {
1157     OnLineJustificationPropertySet(propertyValue);
1158   }
1159   else if( ( index == mPropertyFadeBoundaryLeft )  ||
1160            ( index == mPropertyFadeBoundaryRight ) ||
1161            ( index == mPropertyFadeBoundaryTop )   ||
1162            ( index == mPropertyFadeBoundaryBottom ) )
1163   {
1164     OnFadeBoundaryPropertySet( index, propertyValue );
1165   }
1166   else if( index == mPropertyLineHeightOffset )
1167   {
1168     Dali::PointSize pointSize( propertyValue.Get<float>() );
1169     SetLineHeightOffset(pointSize);
1170   }
1171   else if ( ( index == mPropertyHorizontalAlignment ) ||
1172             ( index == mPropertyVerticalAlignment ) )
1173   {
1174     OnAlignmentPropertySet( index, propertyValue );
1175   }
1176 }
1177
1178 void TextView::OnInitialize()
1179 {
1180   Actor self = Self();
1181
1182   mPropertyText = self.RegisterProperty( PROPERTY_TEXT, "", Property::READ_WRITE );
1183
1184   mPropertyMultilinePolicy = self.RegisterProperty( PROPERTY_MULTILINE_POLICY, "SplitByNewLineChar", Property::READ_WRITE );
1185
1186   mPropertyWidthExceedPolicy = self.RegisterProperty( PROPERTY_WIDTH_EXCEED_POLICY, "Original", Property::READ_WRITE );
1187
1188   mPropertyHeightExceedPolicy = self.RegisterProperty( PROPERTY_HEIGHT_EXCEED_POLICY, "Original", Property::READ_WRITE );
1189
1190   mPropertyLineJustification = self.RegisterProperty( PROPERTY_LINE_JUSTIFICATION, "Left", Property::READ_WRITE );
1191
1192   mPropertyFadeBoundaryLeft = self.RegisterProperty( PROPERTY_FADE_BOUNDARY_LEFT, static_cast< int >( 0 ), Property::READ_WRITE );
1193
1194   mPropertyFadeBoundaryRight = self.RegisterProperty( PROPERTY_FADE_BOUNDARY_RIGHT, static_cast< int >( 0 ), Property::READ_WRITE );
1195
1196   mPropertyFadeBoundaryTop = self.RegisterProperty( PROPERTY_FADE_BOUNDARY_TOP, static_cast< int >( 0 ), Property::READ_WRITE );
1197
1198   mPropertyFadeBoundaryBottom = self.RegisterProperty( PROPERTY_FADE_BOUNDARY_BOTTOM, static_cast< int >( 0 ), Property::READ_WRITE );
1199
1200   mPropertyLineHeightOffset = self.RegisterProperty( PROPERTY_LINE_HEIGHT_OFFSET, 0.0f, Property::READ_WRITE );
1201
1202   mPropertyHorizontalAlignment = self.RegisterProperty( PROPERTY_HORIZONTAL_ALIGNMENT, "HorizontalCenter", Property::READ_WRITE );
1203
1204   mPropertyVerticalAlignment = self.RegisterProperty( PROPERTY_VERTICAL_ALIGNMENT, "VerticalCenter", Property::READ_WRITE );
1205
1206 }
1207
1208
1209 void TextView::OnStyleChange( StyleChange change )
1210 {
1211   mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo = TextViewProcessor::WordLayoutInfo();
1212   TextViewProcessor::CreateWordTextInfo( mLayoutParameters.mEllipsizeText,
1213                                          mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo );
1214
1215   SetText( mCurrentStyledText );
1216 }
1217
1218 void TextView::OnControlSizeSet( const Vector3& size )
1219 {
1220   if( size.GetVectorXY() != mRelayoutData.mTextViewSize )
1221   {
1222     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values.
1223     mRelayoutOperations = RELAYOUT_ALL;
1224
1225     // Request to be relaid out
1226     RelayoutRequest();
1227   }
1228 }
1229
1230 void TextView::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1231 {
1232   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
1233   {
1234     // Not worth to relayout if width or height is equal to zero.
1235     return;
1236   }
1237
1238   if( size != mRelayoutData.mTextViewSize )
1239   {
1240     // if new size is different than the prevoius one, set positions and maybe sizes of all glyph-actor is needed.
1241     if( RELAYOUT_ALL != mRelayoutOperations )
1242     {
1243       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
1244                                                                 RELAYOUT_REMOVE_TEXT_ACTORS |
1245                                                                 RELAYOUT_SIZE_POSITION |
1246                                                                 RELAYOUT_ALIGNMENT |
1247                                                                 RELAYOUT_VISIBILITY |
1248                                                                 RELAYOUT_TEXT_ACTOR_UPDATE |
1249                                                                 RELAYOUT_INSERT_TO_TEXT_VIEW |
1250                                                                 RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
1251     }
1252   }
1253
1254   // Remove glyph-actors from text-view
1255   if( !mRelayoutData.mGlyphActors.empty() && ( mRelayoutOperations & RELAYOUT_REMOVE_TEXT_ACTORS ) )
1256   {
1257     TextViewRelayout::RemoveGlyphActors( GetRootActor(), mRelayoutData.mGlyphActors );
1258     mRelayoutData.mGlyphActors.clear();
1259   }
1260
1261   if( NO_RELAYOUT != mRelayoutOperations )
1262   {
1263     // Relays-out and add glyph-actors to the text-view.
1264     DoRelayOut( size, mRelayoutOperations );
1265     ProcessSnapshot( size );
1266   }
1267
1268   // Quite likely the texts of the text-actors are not going to be reused, so clear them.
1269   mRelayoutData.mTextActorCache.ClearTexts();
1270 }
1271
1272 void TextView::PerformTextViewProcessorOperations()
1273 {
1274   // Traverse the relayout operation vector ...
1275
1276   // Optimizes some operations.
1277   OptimizeTextViewProcessorOperations();
1278
1279   for( std::vector<TextViewProcessorMetadata>::const_iterator it = mTextViewProcessorOperations.begin(), endIt = mTextViewProcessorOperations.end(); it != endIt; ++it )
1280   {
1281     const TextViewProcessorMetadata& relayoutMetadata( *it );
1282
1283     switch( relayoutMetadata.mType )
1284     {
1285       case TextView::TextSet:
1286       {
1287         TextViewProcessor::CreateTextInfo( relayoutMetadata.mText,
1288                                            mLayoutParameters,
1289                                            mRelayoutData );
1290         break;
1291       }
1292       case TextView::TextInserted:
1293       {
1294         TextViewProcessor::UpdateTextInfo( relayoutMetadata.mPosition,
1295                                            relayoutMetadata.mText,
1296                                            mLayoutParameters,
1297                                            mRelayoutData );
1298         break;
1299       }
1300       case TextView::TextReplaced:
1301       {
1302         TextViewProcessor::UpdateTextInfo( relayoutMetadata.mPosition,
1303                                            relayoutMetadata.mNumberOfCharacters,
1304                                            relayoutMetadata.mText,
1305                                            mLayoutParameters,
1306                                            mRelayoutData );
1307         break;
1308       }
1309       case TextView::TextRemoved:
1310       {
1311         TextViewProcessor::UpdateTextInfo( relayoutMetadata.mPosition,
1312                                            relayoutMetadata.mNumberOfCharacters,
1313                                            mLayoutParameters,
1314                                            mRelayoutData,
1315                                            TextViewProcessor::CLEAR_TEXT ); // clears the text of the text-actors.
1316         break;
1317       }
1318       case TextView::NewLineHeight:
1319       {
1320         TextViewProcessor::UpdateTextInfo( mLayoutParameters.mLineHeightOffset,
1321                                            mRelayoutData.mTextLayoutInfo );
1322         break;
1323       }
1324       case TextView::NewStyle:
1325       {
1326         TextViewProcessor::UpdateTextInfo( ( *relayoutMetadata.mText.begin() ).mStyle,
1327                                            relayoutMetadata.mStyleMask,
1328                                            mRelayoutData );
1329         break;
1330       }
1331     }
1332   }
1333
1334   // Clear all operations when they are done.
1335   mTextViewProcessorOperations.clear();
1336 }
1337
1338 void TextView::OptimizeTextViewProcessorOperations()
1339 {
1340   // TODO: check if some operation can be discarted. i.e. InsertTextAt( 0, "a" ); RemoveTextFrom( 0, 1 );
1341
1342   // At the moment it only replaces a 'remove 1 character' followed by 'insert 1 character' in the same position by a 'replace' operation.
1343   // This sequence is used by text-input with predictive text. Change this two operations by a replace allows the text-view processor to
1344   // use the cache without clearing the text-actors.
1345
1346   std::vector<TextViewProcessorMetadata> textViewProcessorOperations;
1347
1348   for( std::vector<TextViewProcessorMetadata>::const_iterator it = mTextViewProcessorOperations.begin(), endIt = mTextViewProcessorOperations.end(); it != endIt; ++it )
1349   {
1350     const TextViewProcessorMetadata& relayoutMetadata( *it );
1351
1352     switch( relayoutMetadata.mType )
1353     {
1354       case TextView::TextRemoved:
1355       {
1356         bool optimizationDone = false;
1357
1358         if( it + 1 != endIt )
1359         {
1360           const TextViewProcessorMetadata& nextRelayoutMetadata( *( it + 1 ) );
1361           if( TextView::TextInserted == nextRelayoutMetadata.mType )
1362           {
1363             if( relayoutMetadata.mPosition == nextRelayoutMetadata.mPosition )
1364             {
1365               optimizationDone = true;
1366               TextViewProcessorMetadata newRelayoutMetadata;
1367               newRelayoutMetadata.mType = TextView::TextReplaced;
1368               newRelayoutMetadata.mPosition = relayoutMetadata.mPosition;
1369               newRelayoutMetadata.mNumberOfCharacters = relayoutMetadata.mNumberOfCharacters;
1370               newRelayoutMetadata.mText = nextRelayoutMetadata.mText;
1371               textViewProcessorOperations.push_back( newRelayoutMetadata );
1372
1373               // do not access the TextInserted operation in next iteration.
1374               ++it;
1375             }
1376           }
1377         }
1378
1379         if( !optimizationDone )
1380         {
1381           textViewProcessorOperations.push_back( relayoutMetadata );
1382         }
1383         break;
1384       }
1385       default:
1386       {
1387         textViewProcessorOperations.push_back( relayoutMetadata );
1388       }
1389     }
1390   }
1391
1392   mTextViewProcessorOperations = textViewProcessorOperations;
1393 }
1394
1395 void TextView::DoRelayOut( const Size& textViewSize, const RelayoutOperationMask relayoutOperationMask )
1396 {
1397   // Traverse the relayout operation vector. It fills the natural size, layout and glyph-actor data structures.
1398   if( !mTextViewProcessorOperations.empty() )
1399   {
1400     PerformTextViewProcessorOperations();
1401   }
1402
1403   CombineExceedPolicies();
1404
1405   Actor rootActor;
1406   if( mVisualParameters.mSnapshotModeEnabled )
1407   {
1408     rootActor = mOffscreenRootActor;
1409   }
1410   else
1411   {
1412     rootActor = Self();
1413   }
1414
1415   mRelayoutData.mTextViewSize = textViewSize;
1416   switch( mLayoutParameters.mMultilinePolicy )
1417   {
1418     case Toolkit::TextView::SplitByNewLineChar:              // multiline policy == SplitByNewLineChar.
1419     {
1420       SplitByNewLineChar::Relayout( rootActor, relayoutOperationMask, mLayoutParameters, mVisualParameters, mRelayoutData );
1421       break;
1422     } // SplitByNewLineChar
1423
1424     case Toolkit::TextView::SplitByWord:                     // multiline policy == SplitByWord.
1425     {
1426       SplitByWord::Relayout( rootActor, relayoutOperationMask, mLayoutParameters, mVisualParameters, mRelayoutData );
1427       break;
1428     } // SplitByWord
1429
1430     case Toolkit::TextView::SplitByChar:                     // multiline policy == SplitByChar.
1431     {
1432       SplitByChar::Relayout( rootActor, relayoutOperationMask, mLayoutParameters, mVisualParameters, mRelayoutData );
1433       break;
1434     } // SplitByChar
1435   } // switch( mMultilinePolicy )
1436
1437   // Remove done operations from the mask.
1438   mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations & ~relayoutOperationMask );
1439 }
1440
1441 void TextView::ProcessSnapshot( const Size& textViewSize )
1442 {
1443   if( mVisualParameters.mSnapshotModeEnabled )
1444   {
1445     // If layout options change, it's needed generate a new image.
1446
1447     if( mOffscreenRootActor )
1448     {
1449       // Set the root actor visible.
1450       // The root actor is set to non visible after the render task is processed.
1451       mOffscreenRootActor.SetVisible( true );
1452
1453       // The offscreen root actor must have same size as text view. Otherwise, text alignment won't work.
1454       mOffscreenRootActor.SetSize( textViewSize );
1455     }
1456
1457     if( ( mRelayoutData.mTextSizeForRelayoutOption.width > Math::MACHINE_EPSILON_1000 ) &&
1458         ( mRelayoutData.mTextSizeForRelayoutOption.height > Math::MACHINE_EPSILON_1000 ) )
1459     {
1460       // Set the image actor visible.
1461       // The image actor is set to non visible if there is no text to render.
1462       mOffscreenImageActor.SetVisible( true );
1463
1464       // Calculates the offscreen image's size. It takes into account different points:
1465       // * If text has italics, add a small offset is needed in order to not to cut the text next to the right edge.
1466       // * There is a maximum texture size the graphic subsystem can load on the memory.
1467       // * If the scroll is enabled, the offscreen image's size is never bigger than the text-view's size.
1468
1469       const Size offscreenSize( std::min( MAX_OFFSCREEN_RENDERING_SIZE,
1470                                           mVisualParameters.mScrollEnabled ? textViewSize.width : std::max( mRelayoutData.mTextSizeForRelayoutOption.width, textViewSize.width ) + mRelayoutData.mTextLayoutInfo.mMaxItalicsOffset ),
1471                                 std::min( MAX_OFFSCREEN_RENDERING_SIZE,
1472                                           mVisualParameters.mScrollEnabled ? textViewSize.height : std::max( mRelayoutData.mTextSizeForRelayoutOption.height, textViewSize.height ) ) );
1473
1474       const bool sizeChanged = offscreenSize != mCurrentOffscreenSize;
1475
1476       if( sizeChanged )
1477       {
1478         // Creates a frame buffer for offscreen rendering when the size is negotiated.
1479         mFrameBufferImage = FrameBufferImage::New( offscreenSize.width,
1480                                                    offscreenSize.height,
1481                                                    Pixel::RGBA8888 );
1482
1483         // Stores current text-view size to avoid create new Dali resources if text changes.
1484         mCurrentOffscreenSize = offscreenSize;
1485
1486         if( !mOffscreenCameraActor )
1487         {
1488           // Creates a new camera actor.
1489           mOffscreenCameraActor = CameraActor::New();
1490           mOffscreenCameraActor.SetParentOrigin( ParentOrigin::CENTER );
1491           mOffscreenCameraActor.SetAnchorPoint( AnchorPoint::CENTER );
1492           mOffscreenCameraActor.SetRotation(Degree(180.f), Vector3::YAXIS);
1493
1494           mOffscreenCameraActor.SetType( Dali::Camera::FREE_LOOK ); // Inherits position from the offscreen root actor.
1495
1496           mOffscreenRootActor.Add( mOffscreenCameraActor ); // camera to shoot the offscreen text
1497         }
1498
1499         // Calculate camera parameters for current text size.
1500         mOffscreenCameraActor.SetOrthographicProjection( offscreenSize );
1501       }
1502
1503       if( mVisualParameters.mScrollEnabled )
1504       {
1505         // Updates the offscreen camera position with the new scroll offset.
1506         mOffscreenCameraActor.SetX( mVisualParameters.mCameraScrollPosition.x );
1507         mOffscreenCameraActor.SetY( mVisualParameters.mCameraScrollPosition.y );
1508       }
1509       else
1510       {
1511         // Text's size could be bigger than text-view's size. In that case the camera must be aligned to cover the whole text.
1512         AlignOffscreenCameraActor( textViewSize, offscreenSize );
1513       }
1514
1515       if( !mRenderTask )
1516       {
1517         // Creates a new render task.
1518         mRenderTask = Stage::GetCurrent().GetRenderTaskList().CreateTask();
1519
1520         mRenderTask.SetSourceActor( mOffscreenRootActor );
1521         mRenderTask.SetInputEnabled( false );
1522         mRenderTask.SetClearColor( Color::TRANSPARENT );
1523         mRenderTask.SetClearEnabled( true );
1524         mRenderTask.SetExclusive( true );
1525
1526         // Connects the signal to the TextView::RenderTaskFinished method in order to make the root actor non visible when the render task is processed.
1527         mRenderTask.FinishedSignal().Connect( this, &TextView::RenderTaskFinished );
1528       }
1529
1530       if( sizeChanged )
1531       {
1532         mRenderTask.SetCameraActor( mOffscreenCameraActor );
1533         mRenderTask.SetTargetFrameBuffer( mFrameBufferImage );
1534       }
1535
1536       // Process the render task only once every time the text changes or the text-view's size canges.
1537       mRenderTask.SetRefreshRate( RenderTask::REFRESH_ONCE );
1538     }
1539     else
1540     {
1541       // If there is no text just make any previous generated image invisible instead to process a render task with no text.
1542       mOffscreenImageActor.SetVisible( false );
1543     }
1544   }
1545 }
1546
1547 void TextView::AlignOffscreenCameraActor( const Size& textViewSize, const Size& offscreenSize )
1548 {
1549   float xPosition = 0.f;
1550   float yPosition = 0.f;
1551   Vector3 parentOrigin = ParentOrigin::CENTER;
1552   Vector3 anchorPoint = AnchorPoint::CENTER;
1553
1554   switch( mLayoutParameters.mHorizontalAlignment )
1555   {
1556     case Toolkit::Alignment::HorizontalLeft:
1557     {
1558       xPosition = 0.5f * ( offscreenSize.width - textViewSize.width );
1559       parentOrigin.x = 0.f;
1560       anchorPoint.x = 0.f;
1561       break;
1562     }
1563     case Toolkit::Alignment::HorizontalCenter:
1564     {
1565       // nothing to do.
1566       break;
1567     }
1568     case Toolkit::Alignment::HorizontalRight:
1569     {
1570       xPosition = 0.5f * ( textViewSize.width - offscreenSize.width );
1571       parentOrigin.x = 1.f;
1572       anchorPoint.x = 1.f;
1573       break;
1574     }
1575     default:
1576     {
1577       DALI_ASSERT_ALWAYS( !"TextView::AlignOffscreenCameraActor: Invalid alignment option." )
1578     }
1579   }
1580
1581   switch( mLayoutParameters.mVerticalAlignment )
1582   {
1583     case Toolkit::Alignment::VerticalTop:
1584     {
1585       yPosition = 0.5f * ( offscreenSize.height - textViewSize.height );
1586       parentOrigin.y = 0.f;
1587       anchorPoint.y = 0.f;
1588       break;
1589     }
1590     case Toolkit::Alignment::VerticalCenter:
1591     {
1592       // nothing to do.
1593       break;
1594     }
1595     case Toolkit::Alignment::VerticalBottom:
1596     {
1597       yPosition = 0.5f * ( textViewSize.height - offscreenSize.height );
1598       parentOrigin.y = 1.f;
1599       anchorPoint.y = 1.f;
1600       break;
1601     }
1602     default:
1603     {
1604       DALI_ASSERT_ALWAYS( !"TextView::AlignOffscreenCameraActor: Invalid alignment option." )
1605     }
1606   }
1607
1608   mOffscreenCameraActor.SetX( xPosition );
1609   mOffscreenCameraActor.SetY( yPosition );
1610
1611   mOffscreenImageActor.SetParentOrigin( parentOrigin );
1612   mOffscreenImageActor.SetAnchorPoint( anchorPoint );
1613 }
1614
1615 void TextView::RenderTaskFinished( Dali::RenderTask& renderTask )
1616 {
1617   // not to process the offscreen root actor by setting its visibility to false.
1618   mOffscreenRootActor.SetVisible( false );
1619
1620   // Sets the new size and the new frame buffer to the image actor.
1621   // Image actor must have same size as text. Otherwise text can be truncated.
1622   mOffscreenImageActor.SetSize( mCurrentOffscreenSize );
1623   mOffscreenImageActor.SetImage( mFrameBufferImage );
1624 }
1625
1626 void TextView::DestroyOffscreenRenderingResources()
1627 {
1628   if( mRenderTask )
1629   {
1630     mRenderTask.FinishedSignal().Disconnect( this, &TextView::RenderTaskFinished );
1631
1632     if( Stage::IsInstalled() )
1633     {
1634       Stage::GetCurrent().GetRenderTaskList().RemoveTask( mRenderTask );
1635     }
1636
1637     mRenderTask.Reset();
1638   }
1639
1640   // Remove and reset the root actor, image actor and camera actor as text-view is not rendering offscreen.
1641   if( mOffscreenCameraActor )
1642   {
1643     mOffscreenRootActor.Remove( mOffscreenCameraActor );
1644
1645     mOffscreenCameraActor.Reset();
1646   }
1647
1648   if( mOffscreenRootActor )
1649   {
1650     mOffscreenRootActor.Reset();
1651   }
1652
1653   if( mOffscreenImageActor )
1654   {
1655     mOffscreenImageActor.Reset();
1656   }
1657
1658   mCurrentOffscreenSize = Size( 0.f, 0.f );
1659
1660   if( mFrameBufferImage )
1661   {
1662     mFrameBufferImage.Reset();
1663   }
1664 }
1665
1666 void TextView::OnTextPan( Actor actor, PanGesture gesture )
1667 {
1668   if( 1u == gesture.numberOfTouches )
1669   {
1670     DoSetScrollPosition( mVisualParameters.mCameraScrollPosition - gesture.displacement );
1671   }
1672 }
1673
1674 void TextView::TrimScrollPosition()
1675 {
1676   const Vector3& textViewSize = GetControlSize();
1677
1678   // Before use the text's size, relayout the text is needed to get the actual text size.
1679   GetTextLayoutInfo();
1680
1681   // Calculates the range within the text could be scrolled. (When the text is aligned in the center).
1682   float maxHorizontalDisplacement = std::max( 0.f, 0.5f * ( mRelayoutData.mTextSizeForRelayoutOption.width - textViewSize.width ) );
1683   float maxVerticalDisplacement = std::max( 0.f, 0.5f * ( mRelayoutData.mTextSizeForRelayoutOption.height - textViewSize.height ) );
1684   float minHorizontalDisplacement = -maxHorizontalDisplacement;
1685   float minVerticalDisplacement = -maxVerticalDisplacement;
1686
1687   // Updates the range if the text is aligned on the right or left.
1688   switch( mLayoutParameters.mHorizontalAlignment )
1689   {
1690     case Toolkit::Alignment::HorizontalLeft:
1691     {
1692       maxHorizontalDisplacement *= 2.f;
1693       minHorizontalDisplacement = 0.f;
1694       break;
1695     }
1696     case Toolkit::Alignment::HorizontalCenter:
1697     {
1698       // nothing to do.
1699       break;
1700     }
1701     case Toolkit::Alignment::HorizontalRight:
1702     {
1703       maxHorizontalDisplacement = 0.f;
1704       minHorizontalDisplacement *= 2.f;
1705       break;
1706     }
1707     default:
1708     {
1709       DALI_ASSERT_ALWAYS( !"TextView::TrimScrollPosition: Invalid alignment option." )
1710     }
1711   }
1712
1713   // Updates the range if the text is aligned on the top or bottom.
1714   switch( mLayoutParameters.mVerticalAlignment )
1715   {
1716     case Toolkit::Alignment::VerticalTop:
1717     {
1718       maxVerticalDisplacement *= 2.f;
1719       minVerticalDisplacement = 0.f;
1720       break;
1721     }
1722     case Toolkit::Alignment::VerticalCenter:
1723     {
1724       //nothing to do
1725       break;
1726     }
1727     case Toolkit::Alignment::VerticalBottom:
1728     {
1729       maxVerticalDisplacement = 0.f;
1730       minVerticalDisplacement *= 2.f;
1731       break;
1732     }
1733     default:
1734     {
1735       DALI_ASSERT_ALWAYS( !"TextView::TrimScrollPosition: Invalid alignment option." )
1736     }
1737   }
1738
1739   // Trims the scroll position to be within the range.
1740   mVisualParameters.mCameraScrollPosition.x = std::min( maxHorizontalDisplacement, mVisualParameters.mCameraScrollPosition.x );
1741   mVisualParameters.mCameraScrollPosition.x = std::max( minHorizontalDisplacement, mVisualParameters.mCameraScrollPosition.x );
1742
1743   mVisualParameters.mCameraScrollPosition.y = std::min( maxVerticalDisplacement, mVisualParameters.mCameraScrollPosition.y );
1744   mVisualParameters.mCameraScrollPosition.y = std::max( minVerticalDisplacement, mVisualParameters.mCameraScrollPosition.y );
1745 }
1746
1747 void TextView::DoSetScrollPosition( const Vector2& position )
1748 {
1749   // Stores old scroll position.
1750   Vector2 delta( mVisualParameters.mCameraScrollPosition );
1751
1752   // Updates the scroll position
1753   mVisualParameters.mCameraScrollPosition = position;
1754
1755   // Ensures the text-view is covered with text.
1756   TrimScrollPosition();
1757
1758   // Calculate the difference with the previous scroll position
1759   delta.x = mVisualParameters.mCameraScrollPosition.x - delta.x;
1760   delta.y = mVisualParameters.mCameraScrollPosition.y - delta.y;
1761
1762   if( mOffscreenRootActor )
1763   {
1764     // If there is a render-task it needs to be refreshed. Therefore glyph-actors need to be
1765     // set to visible.
1766     mOffscreenRootActor.SetVisible( true );
1767   }
1768
1769   if( mOffscreenCameraActor )
1770   {
1771     // Update the offscreen camera with the new scroll position.
1772     mOffscreenCameraActor.SetX( mVisualParameters.mCameraScrollPosition.x );
1773     mOffscreenCameraActor.SetY( mVisualParameters.mCameraScrollPosition.y );
1774   }
1775
1776   if( mRenderTask )
1777   {
1778     // Refresh the render-task.
1779     mRenderTask.SetRefreshRate( RenderTask::REFRESH_ONCE );
1780   }
1781
1782   // Emit the signal.
1783   Toolkit::TextView handle( GetOwner() );
1784   mScrolledSignalV2.Emit( handle, delta );
1785 }
1786
1787 void TextView::CombineExceedPolicies()
1788 {
1789   // Calculates the combination of exceed policies.
1790
1791   switch( mLayoutParameters.mWidthExceedPolicy )
1792   {
1793     case Toolkit::TextView::Original:
1794     {
1795       switch( mLayoutParameters.mHeightExceedPolicy )
1796       {
1797         case Toolkit::TextView::Original:
1798         {
1799           mLayoutParameters.mExceedPolicy = Original;
1800           break;
1801         }
1802         case Toolkit::TextView::Fade:
1803         {
1804           mLayoutParameters.mExceedPolicy = OriginalFade;
1805           break;
1806         }
1807         case Toolkit::TextView::ShrinkToFit:
1808         {
1809           mLayoutParameters.mExceedPolicy = OriginalShrink;
1810           break;
1811         }
1812         default:
1813         {
1814           DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" );
1815         }
1816       }
1817       break;
1818     }
1819     case Toolkit::TextView::Split:
1820     {
1821       switch( mLayoutParameters.mHeightExceedPolicy )
1822       {
1823         case Toolkit::TextView::Original:
1824         {
1825           mLayoutParameters.mExceedPolicy = SplitOriginal;
1826           break;
1827         }
1828         case Toolkit::TextView::Fade:
1829         {
1830           mLayoutParameters.mExceedPolicy = SplitFade;
1831           break;
1832         }
1833         case Toolkit::TextView::ShrinkToFit:
1834         {
1835           mLayoutParameters.mExceedPolicy = SplitShrink;
1836           break;
1837         }
1838         default:
1839         {
1840           DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" );
1841         }
1842       }
1843       break;
1844     }
1845     case Toolkit::TextView::Fade:
1846     {
1847       switch( mLayoutParameters.mHeightExceedPolicy )
1848       {
1849         case Toolkit::TextView::Original:
1850         {
1851           mLayoutParameters.mExceedPolicy = FadeOriginal;
1852           break;
1853         }
1854         case Toolkit::TextView::Fade:
1855         {
1856           mLayoutParameters.mExceedPolicy = Fade;
1857           break;
1858         }
1859         default:
1860         {
1861           DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" );
1862         }
1863       }
1864       break;
1865     }
1866     case Toolkit::TextView::ShrinkToFit:
1867     {
1868       switch( mLayoutParameters.mHeightExceedPolicy )
1869       {
1870         case Toolkit::TextView::Original:
1871         {
1872           mLayoutParameters.mExceedPolicy = ShrinkOriginal;
1873           break;
1874         }
1875         case Toolkit::TextView::Fade:
1876         {
1877           mLayoutParameters.mExceedPolicy = ShrinkFade;
1878           break;
1879         }
1880         case Toolkit::TextView::ShrinkToFit:
1881         {
1882           mLayoutParameters.mExceedPolicy = Shrink;
1883           break;
1884         }
1885         default:
1886         {
1887           DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" );
1888         }
1889       }
1890       break;
1891     }
1892     case Toolkit::TextView::EllipsizeEnd:
1893     {
1894       switch( mLayoutParameters.mHeightExceedPolicy )
1895       {
1896         case Toolkit::TextView::Original:
1897         {
1898           mLayoutParameters.mExceedPolicy = EllipsizeEndOriginal;
1899           break;
1900         }
1901         case Toolkit::TextView::EllipsizeEnd:
1902         {
1903           mLayoutParameters.mExceedPolicy = EllipsizeEnd;
1904           break;
1905         }
1906         default:
1907         {
1908           DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" );
1909         }
1910       }
1911       break;
1912     }
1913     default:
1914     {
1915       DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width exceed policy" );
1916     }
1917   }
1918 }
1919
1920 Actor TextView::GetRootActor() const
1921 {
1922   // Get the root actor, if text-view was rendering offscreen, or the text-view itself.
1923
1924   Actor rootActor;
1925
1926   if( mVisualParameters.mSnapshotModeEnabled )
1927   {
1928     rootActor = mOffscreenRootActor;
1929   }
1930   else
1931   {
1932     rootActor = Self();
1933   }
1934
1935   return rootActor;
1936 }
1937
1938 void TextView::OnMultilinePolicyPropertySet( Property::Value propertyValue )
1939 {
1940   std::string policyName( propertyValue.Get<std::string>() );
1941   if(policyName == "SplitByNewLineChar")
1942   {
1943     SetMultilinePolicy(Toolkit::TextView::SplitByNewLineChar);
1944   }
1945   else if(policyName == "SplitByWord")
1946   {
1947     SetMultilinePolicy(Toolkit::TextView::SplitByWord);
1948   }
1949   else if(policyName == "SplitByChar")
1950   {
1951     SetMultilinePolicy(Toolkit::TextView::SplitByChar);
1952   }
1953   else
1954   {
1955     DALI_ASSERT_ALWAYS( !"TextView::OnMultilinePolicyPropertySet(). Invalid Property value." );
1956   }
1957 }
1958
1959 void TextView::OnWidthExceedPolicyPropertySet( Property::Value propertyValue )
1960 {
1961   std::string policyName( propertyValue.Get<std::string>() );
1962   if(policyName == "Original")
1963   {
1964     SetWidthExceedPolicy(Toolkit::TextView::Original);
1965   }
1966   else if(policyName == "Truncate")
1967   {
1968     SetWidthExceedPolicy(Toolkit::TextView::Truncate);
1969   }
1970   else if(policyName == "Fade")
1971   {
1972     SetWidthExceedPolicy(Toolkit::TextView::Fade);
1973   }
1974   else if(policyName == "Split")
1975   {
1976     SetWidthExceedPolicy(Toolkit::TextView::Split);
1977   }
1978   else if(policyName == "ShrinkToFit")
1979   {
1980     SetWidthExceedPolicy(Toolkit::TextView::ShrinkToFit);
1981   }
1982   else if(policyName == "EllipsizeEnd")
1983   {
1984     SetWidthExceedPolicy(Toolkit::TextView::EllipsizeEnd);
1985   }
1986   else
1987   {
1988     DALI_ASSERT_ALWAYS( !"TextView::OnWidthExceedPolicyPropertySet(). Invalid Property value." );
1989   }
1990 }
1991
1992 void TextView::OnHeightExceedPolicyPropertySet( Property::Value propertyValue )
1993 {
1994   std::string policyName( propertyValue.Get<std::string>() );
1995   if(policyName == "Original")
1996   {
1997     SetHeightExceedPolicy(Toolkit::TextView::Original);
1998   }
1999   else if(policyName == "Truncate")
2000   {
2001     SetHeightExceedPolicy(Toolkit::TextView::Truncate);
2002   }
2003   else if(policyName == "Fade")
2004   {
2005     SetHeightExceedPolicy(Toolkit::TextView::Fade);
2006   }
2007   else if(policyName == "Split")
2008   {
2009     SetHeightExceedPolicy(Toolkit::TextView::Split);
2010   }
2011   else if(policyName == "ShrinkToFit")
2012   {
2013     SetHeightExceedPolicy(Toolkit::TextView::ShrinkToFit);
2014   }
2015   else
2016   {
2017     DALI_ASSERT_ALWAYS( !"TextView::OnHeightExceedPolicyPropertySet(). Invalid Property value." );
2018   }
2019 }
2020
2021 void TextView::OnLineJustificationPropertySet( Property::Value propertyValue )
2022 {
2023   std::string policyName( propertyValue.Get<std::string>() );
2024   if(policyName == "Left")
2025   {
2026     SetLineJustification(Toolkit::TextView::Left);
2027   }
2028   else if(policyName == "Center")
2029   {
2030     SetLineJustification(Toolkit::TextView::Center);
2031   }
2032   else if(policyName == "Right")
2033   {
2034     SetLineJustification(Toolkit::TextView::Right);
2035   }
2036   else if(policyName == "Justified")
2037   {
2038     SetLineJustification(Toolkit::TextView::Justified);
2039   }
2040   else
2041   {
2042     DALI_ASSERT_ALWAYS( !"TextView::OnLineJustificationPropertySet(). Invalid Property value." );
2043   }
2044 }
2045
2046 void TextView::OnFadeBoundaryPropertySet( Property::Index propertyIndex, Property::Value propertyValue )
2047 {
2048   PixelSize boundary( propertyValue.Get<unsigned int>() );
2049
2050   if ( propertyIndex == mPropertyFadeBoundaryLeft )
2051   {
2052     mVisualParameters.mFadeBoundary.mLeft = boundary;
2053   }
2054   else if ( propertyIndex == mPropertyFadeBoundaryRight )
2055   {
2056     mVisualParameters.mFadeBoundary.mRight = boundary;
2057   }
2058   else if ( propertyIndex == mPropertyFadeBoundaryTop )
2059   {
2060     mVisualParameters.mFadeBoundary.mTop = boundary;
2061   }
2062   else if ( propertyIndex == mPropertyFadeBoundaryBottom )
2063   {
2064     mVisualParameters.mFadeBoundary.mBottom = boundary;
2065   }
2066   else
2067   {
2068     DALI_ASSERT_ALWAYS( !"TextView::OnFadeBoundaryPropertySet(). Invalid Property value." );
2069   }
2070 }
2071
2072 void TextView::OnAlignmentPropertySet( Property::Index propertyIndex, Property::Value propertyValue )
2073 {
2074   std::string value( propertyValue.Get<std::string>() );
2075
2076   if( propertyIndex == mPropertyHorizontalAlignment )
2077   {
2078     if(value == "HorizontalLeft")
2079     {
2080       mLayoutParameters.mHorizontalAlignment = Toolkit::Alignment::HorizontalLeft;
2081     }
2082     else if( value == "HorizontalCenter")
2083     {
2084       mLayoutParameters.mHorizontalAlignment = Toolkit::Alignment::HorizontalCenter;
2085     }
2086     else if( value == "HorizontalRight")
2087     {
2088       mLayoutParameters.mHorizontalAlignment = Toolkit::Alignment::HorizontalRight;
2089     }
2090     else
2091     {
2092       DALI_ASSERT_ALWAYS( !"TextView::OnAlignmentPropertySet(). Invalid Property value." );
2093     }
2094   }
2095   else if( propertyIndex == mPropertyVerticalAlignment )
2096   {
2097     if( value == "VerticalTop" )
2098     {
2099       mLayoutParameters.mVerticalAlignment = Toolkit::Alignment::VerticalTop;
2100     }
2101     else if( value == "VerticalCenter")
2102     {
2103       mLayoutParameters.mVerticalAlignment = Toolkit::Alignment::VerticalCenter;
2104     }
2105     else if( value == "VerticalBottom")
2106     {
2107       mLayoutParameters.mVerticalAlignment = Toolkit::Alignment::VerticalBottom;
2108     }
2109     else
2110     {
2111       DALI_ASSERT_ALWAYS( !"TextView::OnAlignmentPropertySet(). Invalid Property value." );
2112     }
2113   }
2114 }
2115
2116 void TextView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
2117 {
2118   Toolkit::TextView textView = Toolkit::TextView::DownCast( Dali::BaseHandle( object ) );
2119
2120   if ( textView && ( index == Toolkit::TextView::PROPERTY_MARKUP_ENABLED ) )
2121   {
2122     Internal::TextView& textViewImpl( GetImpl( textView ) );
2123     bool newValue( value.Get<bool>() );
2124     if( newValue != textViewImpl.IsMarkupProcessingEnabled() )
2125     {
2126       textViewImpl.SetMarkupProcessingEnabled( newValue );
2127       if( newValue )
2128       {
2129         // If markup processing has been enabled, Ensure current text is reprocessed.
2130         const std::string& currentText( textViewImpl.GetText() );
2131         if( ! currentText.empty() )
2132         {
2133           textViewImpl.SetText( currentText );
2134         }
2135       }
2136     }
2137   }
2138 }
2139
2140 Property::Value TextView::GetProperty( BaseObject* object, Property::Index propertyIndex )
2141 {
2142   Toolkit::TextView textView = Toolkit::TextView::DownCast( Dali::BaseHandle( object ) );
2143
2144   if ( textView && ( propertyIndex == Toolkit::TextView::PROPERTY_MARKUP_ENABLED ) )
2145   {
2146     return Property::Value( GetImpl( textView ).mMarkUpEnabled );
2147   }
2148
2149   return Property::Value();
2150 }
2151
2152 } // namespace Internal
2153
2154 } // namespace Toolkit
2155
2156 } // namespace Dali