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