Update toolkit with ResourceImage/Image split
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / text-input / text-input-decorator-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-input/text-input-decorator-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/adaptor-framework/clipboard.h>
23 #include <dali/public-api/common/stage.h>
24 #include <dali/public-api/events/pan-gesture.h>
25 #include <dali/public-api/object/property-notification.h>
26 #include <dali/integration-api/debug.h>
27 #include <dali/public-api/images/resource-image.h>
28
29 // INTERNAL INCLUDES
30 #include <dali-toolkit/internal/controls/text-input/text-input-handles-impl.h>
31
32 using namespace Dali;
33
34 namespace
35 {
36 #if defined(DEBUG_ENABLED)
37   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXT_INPUT_DECORATOR");
38 #endif
39
40 const Vector3 DEFAULT_SELECTION_HANDLE_SIZE( 51.0f, 79.0f, 0.0f );
41 const float TOP_HANDLE_TOP_OFFSET(-1.5f);                                   // Offset between top handle and cutCopyPaste pop-up
42 const float BOTTOM_HANDLE_BOTTOM_OFFSET(1.5f);                              // Offset between bottom handle and cutCopyPaste pop-up
43 const float UI_Z_OFFSET( 0.2f );                                            // Text Selection Handles/Cursor z-offset.
44 const Vector3 UI_OFFSET(0.0f, 0.0f, UI_Z_OFFSET);                           // Text Selection Handles/Cursor offset.
45 const char* DEFAULT_CURSOR( DALI_IMAGE_DIR "cursor.png" );
46 const Vector4 DEFAULT_CURSOR_IMAGE_9_BORDER( 2.0f, 2.0f, 2.0f, 2.0f );
47 const std::size_t CURSOR_BLINK_INTERVAL = 500;                              // Cursor blink interval
48 const float CURSOR_THICKNESS(6.0f);
49 const Degree CURSOR_ANGLE_OFFSET(2.0f);                                     // Offset from the angle
50
51 const unsigned int SCROLL_TICK_INTERVAL = 50u;
52 const float SCROLL_THRESHOLD = 10.f;
53 const float SCROLL_SPEED = 15.f;
54
55 /**
56  * Whether the given position plus the cursor size offset is inside the given boundary.
57  *
58  * @param[in] position The given position.
59  * @param[in] cursorSize The cursor size.
60  * @param[in] controlSize The given boundary.
61  * @param[in] threshold imaginary indent around boundary that will trigger the position to be outside of control.
62  *
63  * @return whether the given position is inside the given boundary.
64  */
65 bool IsPositionWithinControl( const Vector3& position, const Size& cursorSize, const Vector3& controlSize, const Vector2 threshold = Vector2::ZERO )
66 {
67   return ( position.x >= -Math::MACHINE_EPSILON_1000 + threshold.x ) &&
68          ( position.x <= controlSize.width - threshold.x + Math::MACHINE_EPSILON_1000 ) &&
69          ( position.y - cursorSize.height >= -Math::MACHINE_EPSILON_1000 + threshold.y ) &&
70          ( position.y <= controlSize.height + Math::MACHINE_EPSILON_1000 - threshold.y);
71 }
72
73 }
74
75 namespace Dali
76 {
77
78 namespace Toolkit
79 {
80
81 namespace Internal
82 {
83
84 Decorator::Decorator( TextViewCharacterPositioning& textViewManager, TextInputTextStyle& textStyle ):
85   mTextViewCharacterPositioning( textViewManager ),
86   mTextStyle( textStyle ),
87   mSelectionHandleOnePosition(0),
88   mSelectionHandleTwoPosition(0),
89   mGrabHandlePosition(0),
90   mCursorPosition( 0 ),
91   mTextHighlight( textViewManager ),
92   mCursorBlinkStatus( true ),
93   mCursorVisibility( true ),
94   mCursorRTLEnabled( false ),
95   mIsGrabHandleInScrollArea( false ),
96   mIsCursorInScrollArea( false ),
97   mGrabHandleVisibility( false ),
98   mGrabHandleEnabled( true )
99 {
100 }
101
102 Decorator::~Decorator()
103 {
104 }
105
106 /**
107  * Bounding Box
108  */
109 void Decorator::SetBoundingBox( const Rect<float>& boundingRectangle )
110 {
111   // Convert to world coordinates and store as a Vector4 to be compatible with Property Notifications.
112   Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
113
114   const float originX = boundingRectangle.x - 0.5f * stageSize.width;
115   const float originY = boundingRectangle.y - 0.5f * stageSize.height;
116
117   const Vector4 boundary( originX,
118                           originY,
119                           originX + boundingRectangle.width,
120                           originY + boundingRectangle.height );
121
122   mBoundingRectangleWorldCoordinates = boundary;
123 }
124
125 Vector4 Decorator::GetBoundingBox() const
126 {
127   return mBoundingRectangleWorldCoordinates;
128 }
129
130 /**
131  * Selection Handles
132  */
133 void Decorator::OnHandlePan(Actor actor, const PanGesture& gesture)
134 {
135   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
136   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
137
138   switch (gesture.state)
139   {
140     case Gesture::Started:
141     // fall through so code not duplicated
142     case Gesture::Continuing:
143     {
144       if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
145       {
146         MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
147         HidePopUp();
148       }
149       else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
150       {
151         MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
152         HidePopUp();
153       }
154       else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
155       {
156         SetCursorVisibility( true );
157         ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
158         MoveGrabHandle( gesture.displacement );
159         HidePopUp(); // Do not show popup while handle is moving
160       }
161     }
162     break;
163
164     case Gesture::Finished:
165     {
166       // Revert back to non-pressed selection handle images
167       if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleOne() )
168       {
169          mSelectionHandleOneActualPosition = MoveSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition, gesture.displacement );
170         ShowPopupCutCopyPaste();
171       }
172       else if ( actor.GetParent() == mTextInputHandles.GetSelectionHandleTwo() )
173       {
174         mSelectionHandleTwoActualPosition = MoveSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition, gesture.displacement );
175         ShowPopupCutCopyPaste();
176       }
177       else if ( actor.GetParent() == mTextInputHandles.GetGrabHandle() )
178       {
179         MoveGrabHandle( gesture.displacement );
180         SetCursorVisibility( true );
181         ShowPopupCutCopyPaste();
182       }
183     }
184     break;
185     default:
186       break;
187   }
188 }
189
190 void Decorator::CreateSelectionHandles( Actor targetParent )
191 {
192   if ( !mPanGestureDetector )
193   {
194     mPanGestureDetector = PanGestureDetector::New();
195     mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
196   }
197
198   if ( !mTextInputHandles.GetSelectionHandleOne() )
199   {
200     mTextInputHandles.CreateSelectionHandles();
201
202     mTextInputHandles.AttachSelectionHandlesToGivenPanGesture( mPanGestureDetector );
203
204     targetParent.Add( mTextInputHandles.GetSelectionHandleOne() );
205     targetParent.Add( mTextInputHandles.GetSelectionHandleTwo() );
206
207     SetUpHandlePropertyNotifications();
208   }
209 }
210
211 void Decorator::RemoveSelectionHandles()
212 {
213   mTextInputHandles.DestorySelectionHandles();
214 }
215
216 Vector3 Decorator::GetSelectionHandleSize()
217 {
218   return DEFAULT_SELECTION_HANDLE_SIZE;
219 }
220
221 std::size_t Decorator::GetHandleOnePosition() const
222 {
223   return mSelectionHandleOnePosition;
224 }
225
226 std::size_t Decorator::GetHandleTwoPosition() const
227 {
228   return mSelectionHandleTwoPosition;
229 }
230
231 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, std::size_t position )
232 {
233   bool direction(false);
234   Vector3 alternatePosition;
235   bool alternatePositionValid(false);
236
237   Vector3 actualPositionOfSelectionHandle = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( position, direction, alternatePosition,alternatePositionValid );
238
239   return PositionSelectionHandle( selectionHandle, actualPositionOfSelectionHandle, position );
240
241 }
242
243 Vector3 Decorator::PositionSelectionHandle( Actor selectionHandle, Vector3& actualPosition, std::size_t position )
244 {
245   const Vector3 DEFAULT_HANDLE_OFFSET(0.0f, -5.0f, 0.0f);
246
247   selectionHandle.SetPosition( actualPosition += DEFAULT_HANDLE_OFFSET );
248
249   return actualPosition;
250 }
251
252 void Decorator::SetSelectionHandlesVisibility(bool visible )
253 {
254   mTextInputHandles.SetSelectionHandleOneVisibility( visible );
255   mTextInputHandles.SetSelectionHandleTwoVisibility( visible );
256 }
257
258 void Decorator::PositionSelectionHandles( std::size_t start, std::size_t end )
259 {
260   mSelectionHandleOnePosition = start;
261   mSelectionHandleTwoPosition = end;
262
263   mTextViewCharacterPositioning.UpdateTextLayoutInfo();
264
265   mSelectionHandleOneActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleOne(), mSelectionHandleOnePosition );
266   mSelectionHandleTwoActualPosition = PositionSelectionHandle( mTextInputHandles.GetSelectionHandleTwo(), mSelectionHandleTwoPosition );
267 }
268
269 Vector3 Decorator::MoveSelectionHandle( Actor selectionHandle,
270                                                           Vector3& actualSelectionHandlePosition,
271                                                           std::size_t& currentSelectionHandlePosition,
272                                                           const Vector2& displacement )
273 {
274   Vector3 actualHandlePosition;
275   actualSelectionHandlePosition.x += displacement.x * selectionHandle.GetCurrentScale().x;
276   actualSelectionHandlePosition.y += displacement.y * selectionHandle.GetCurrentScale().y;
277
278   // Selection handles should jump to the nearest character
279   std::size_t newHandlePosition = 0;
280   newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY() );
281
282   bool direction(false);
283   Vector3 alternatePosition;
284   bool alternatePositionValid(false);
285   actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition,direction, alternatePosition, alternatePositionValid  );
286
287   bool handleVisible = true;
288
289   if ( handleVisible &&                                          // Ensure the handle is visible.
290      ( newHandlePosition != currentSelectionHandlePosition ) &&  // Ensure the handle has moved.
291      ( newHandlePosition != mSelectionHandleTwoPosition ) &&     // Ensure new handle position not the same position as an existing handle.
292      ( newHandlePosition != mSelectionHandleOnePosition ) )
293   {
294     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::MoveSelectionHandle Handle visible and moved]\n");
295
296     currentSelectionHandlePosition = newHandlePosition;
297
298     PositionSelectionHandle( selectionHandle, actualHandlePosition, newHandlePosition );
299
300     ShowUpdatedHighlight();
301
302     // Set Active Style to that of first character in selection
303     std::size_t firstHandleInSelection = std::min( mSelectionHandleOnePosition, mSelectionHandleTwoPosition );
304
305     const TextStyle inputStyle = mTextViewCharacterPositioning.GetStyleAt( firstHandleInSelection );
306     mTextStyle.SetInputStyle( inputStyle );
307   }
308   return actualHandlePosition; // Returns Handle position passed in if new value not assigned.
309 }
310
311 /**
312  * GrabHandle
313  */
314 void Decorator::PositionGrabHandle( std::size_t positionInText )
315 {
316   bool direction(false);
317   Vector3 alternatePosition;
318   bool alternatePositionValid(false);
319
320   mGrabHandlePosition = positionInText;
321
322   mTextViewCharacterPositioning.UpdateTextLayoutInfo();
323   mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( positionInText, direction, alternatePosition,alternatePositionValid );
324
325   mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
326 }
327
328 void Decorator::MoveGrabHandle( const Vector2& displacement /*, std::size_t currentHandlePosition */)
329 {
330   mActualGrabHandlePosition.x += displacement.x;
331   mActualGrabHandlePosition.y += displacement.y;
332
333   // Grab handle should jump to the nearest character and take cursor with it
334   std::size_t newHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
335
336   Vector3 actualHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandlePosition );
337
338   bool handleVisible = true;
339
340   if( ( newHandlePosition != mGrabHandlePosition ) &&                           // Only redraw cursor and do updates if position changed
341         ( handleVisible ) )// and the new position is visible (if scroll is not enabled, it's always true).
342     {
343       mActualGrabHandlePosition = actualHandlePosition;
344       mTextInputHandles.GetGrabHandle().SetPosition( mActualGrabHandlePosition );
345
346       //PositionGrabHandle( newHandlePosition );
347       mGrabHandlePosition = newHandlePosition;
348       SetCurrentCursorPosition( mGrabHandlePosition );
349       DrawCursor( mGrabHandlePosition );
350
351       const std::size_t cursorPosition = GetCurrentCursorPosition();
352
353       // Let keyboard know the new cursor position so can 're-capture' for prediction.
354       mCursorRePositionedSignal.Emit();
355
356       // Set Input Style to that of cursor position
357       if ( !mTextViewCharacterPositioning.IsStyledTextEmpty()  && ( cursorPosition > 0 ) )
358       {
359         DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
360       }
361     }
362 }
363
364 void Decorator::ShowGrabHandle( bool visible )
365 {
366   mGrabHandleVisibility = visible;
367   mTextInputHandles.SetGrabHandleVisibility( visible );
368 }
369
370 void Decorator::CreateGrabHandle( Actor targetParent )
371 {
372   if ( !mPanGestureDetector )
373   {
374     mPanGestureDetector = PanGestureDetector::New();
375     mPanGestureDetector.DetectedSignal().Connect(this, &Decorator::OnHandlePan);
376   }
377
378   if ( !mTextInputHandles.GetGrabHandle() )
379   {
380     mTextInputHandles.CreateGrabHandle();
381     mTextInputHandles.AttachGrabHandleToGivenPanGesture( mPanGestureDetector );
382     targetParent.Add( mTextInputHandles.GetGrabHandle() );
383   }
384 }
385
386 void Decorator::SetGrabHandleImage( Image image )
387 {
388   mTextInputHandles.SetGrabHandleImage( image );
389 }
390
391 void Decorator::EnableGrabHandle( bool toggle)
392 {
393   // enables grab handle with will in turn de-activate magnifier
394   mGrabHandleEnabled = toggle;
395 }
396
397 bool Decorator::IsGrabHandleEnabled()
398 {
399   // if false then magnifier will be shown instead.
400   return mGrabHandleEnabled;
401 }
402
403 /**
404  * Cursor
405  */
406 std::size_t Decorator::GetCurrentCursorPosition() const
407 {
408   return mCursorPosition;
409 }
410
411 void Decorator::SetCurrentCursorPosition( std::size_t newCursorPosition )
412 {
413   mCursorPosition = newCursorPosition;
414 }
415
416 void Decorator::SetCursorVisibility( bool visible )
417 {
418   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::SetCursorVisibility[%s]\n", ( visible )?"true":"false");
419
420   mCursorVisibility = visible;
421   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
422   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
423 }
424
425 void Decorator::DrawCursor(const std::size_t nthChar)
426 {
427   std::size_t cursorPosition = GetCurrentCursorPosition();
428
429   // Get height of cursor and set its size
430   Size size( CURSOR_THICKNESS, 0.0f );
431
432   Vector2 min, max; // out parameters for GetRowRectFromCharacterPosition
433   size.height = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mTextViewCharacterPositioning.GetVisualPosition( cursorPosition ), min, max  ).height;
434
435   mCursor.SetSize(size);
436
437   // If the character is italic then the cursor also tilts.
438   if ( !mTextViewCharacterPositioning.IsStyledTextEmpty()  && ( cursorPosition > 0 ) )
439   {
440     DALI_ASSERT_DEBUG( ( 0 <= cursorPosition-1 ) && ( cursorPosition-1 < mTextViewCharacterPositioning.StyledTextSize() ) );
441     const TextStyle styleAtCursor = mTextViewCharacterPositioning.GetStyleAt( cursorPosition-1 );
442     mCursor.SetRotation( styleAtCursor.IsItalicsEnabled() ? Degree( styleAtCursor.GetItalicsAngle() - CURSOR_ANGLE_OFFSET ) : Degree( 0.f ), Vector3::ZAXIS );
443   }
444
445   DALI_ASSERT_DEBUG( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() );
446   if ( ( cursorPosition <= mTextViewCharacterPositioning.GetNumberOfCharactersInText() ) )
447   {
448     Vector3 altPosition;    // Alternate (i.e. opposite direction) cursor position.
449     bool altPositionValid( false );  // Alternate cursor validity flag.
450     bool directionRTL( false );      // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently)
451     Vector3 position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition, directionRTL, altPosition, altPositionValid );
452
453     SetAltCursorEnabled( altPositionValid );
454
455     mCursor.SetPosition( position + UI_OFFSET );
456   }
457 }
458
459 void Decorator::SetAltCursorEnabled( bool enabled )
460 {
461   mCursorRTLEnabled = enabled;
462   mCursorRTL.SetVisible( mCursorVisibility && mCursorRTLEnabled );
463 }
464
465 void Decorator::SetCursorImage(Dali::Image image, const Vector4& border )
466 {
467   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
468
469   if ( image )
470   {
471     mCursor.SetImage( image );
472     mCursor.SetNinePatchBorder( border );
473   }
474 }
475
476 void Decorator::SetRTLCursorImage( Image image, const Vector4& border )
477 {
478   DALI_ASSERT_DEBUG ( image && "Create cursor image invalid")
479
480   if ( image )
481   {
482     mCursorRTL.SetImage( image );
483     mCursorRTL.SetNinePatchBorder(  border );
484   }
485 }
486
487 ImageActor Decorator::CreateCursor( Image cursorImage, const Vector4& border, const std::string& cursorName )
488 {
489   ImageActor cursor;
490
491   if ( cursorImage )
492   {
493     cursor = ImageActor::New( cursorImage );
494   }
495   else
496   {
497     cursor = ImageActor::New( ResourceImage::New( DEFAULT_CURSOR ) );
498   }
499
500   cursor.SetStyle(ImageActor::STYLE_NINE_PATCH);
501   cursor.SetNinePatchBorder( border );
502   cursor.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
503   cursor.SetVisible(false);
504   cursor.SetName( cursorName );
505   return cursor;
506 }
507
508 void Decorator::CreateCursors( Actor targetParent )
509 {
510   Image mCursorImage = ResourceImage::New( DEFAULT_CURSOR );
511   mCursor = CreateCursor (mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER , "mainCursor");
512   mCursorRTL = CreateCursor ( mCursorImage, DEFAULT_CURSOR_IMAGE_9_BORDER, "rtlCursor");
513   targetParent.Add( mCursor );
514   targetParent.Add( mCursorRTL );
515 }
516
517 Size Decorator::GetCursorSizeAt( std::size_t positionWithinTextToGetCursorSize )
518 {
519   std::size_t visualPosition = mTextViewCharacterPositioning.GetVisualPosition( positionWithinTextToGetCursorSize );
520
521   Vector2 min, max;
522
523   const Size cursorSize( CURSOR_THICKNESS,
524       mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( visualPosition, min, max ).height );
525
526   return cursorSize;
527 }
528
529 void Decorator::StartCursorBlinkTimer()
530 {
531   if ( !mCursorBlinkTimer )
532   {
533     mCursorBlinkTimer = Timer::New( CURSOR_BLINK_INTERVAL );
534     mCursorBlinkTimer.TickSignal().Connect( this, &Decorator::OnCursorBlinkTimerTick );
535   }
536
537   if ( !mCursorBlinkTimer.IsRunning() )
538   {
539     mCursorBlinkTimer.Start();
540   }
541 }
542
543 void Decorator::StopCursorBlinkTimer()
544 {
545   if ( mCursorBlinkTimer )
546   {
547     mCursorBlinkTimer.Stop();
548   }
549 }
550
551 bool Decorator::OnCursorBlinkTimerTick()
552 {
553   // Cursor blinking
554   mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
555   if ( mCursorRTLEnabled )
556   {
557     mCursorRTL.SetVisible( mCursorVisibility && mIsCursorInScrollArea && mCursorBlinkStatus );
558   }
559   mCursorBlinkStatus = !mCursorBlinkStatus;
560
561   return true;
562 }
563
564 /**
565  * Highlight
566  */
567 void Decorator::ShowUpdatedHighlight()
568 {
569   Toolkit::TextView::TextLayoutInfo textLayoutInfo = mTextViewCharacterPositioning.GetLayoutInfo();
570   TextHighlight::HighlightInfo highlightInfo = mTextHighlight.CalculateHighlightInfo( mSelectionHandleOnePosition, mSelectionHandleTwoPosition, textLayoutInfo );
571
572   // Clamp highlightInfo so they don't exceed the boundary of the control.
573   const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize();
574   highlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) );
575
576   mTextHighlight.UpdateHighlight( highlightInfo );
577 }
578
579 void Decorator::CreateHighlight( Actor parent )
580 {
581   DALI_ASSERT_DEBUG( parent && "Highlight target parent does not exist" );
582
583   if ( !mHighlightMeshActor )
584   {
585     mHighlightMeshActor = MeshActor::New( mTextHighlight.CreateHighLightMesh() );
586     mHighlightMeshActor.SetName( "HighlightMeshActor" );
587     mHighlightMeshActor.SetAffectedByLighting(false);
588     parent.Add( mHighlightMeshActor );
589   }
590 }
591
592 void Decorator::RemoveHighlight()
593 {
594   if ( mHighlightMeshActor )
595   {
596     mHighlightMeshActor.Unparent();
597     mHighlightMeshActor.Reset();
598     // NOTE: We cannot dereference mHighlightMesh, due to a how the scene-graph MeshRenderer uses the Mesh data.
599   }
600 }
601
602 void Decorator::HighlightVisibility( bool visiblility )
603 {
604   if ( mHighlightMeshActor )
605   {
606     mHighlightMeshActor.SetVisible( visiblility );
607   }
608 }
609
610 /**
611  *  Callbacks connected to be Property notifications for Boundary checking.
612  */
613 // Note If PropertyNotification signal definition included Actor we would not need to duplicate functions.
614 void Decorator::OnHandleOneLeavesBoundary( PropertyNotification& source)
615 {
616   mTextInputHandles.GetSelectionHandleOne().SetOpacity(0.0f);
617 }
618
619 void Decorator::OnHandleOneWithinBoundary(PropertyNotification& source)
620 {
621   mTextInputHandles.GetSelectionHandleOne().SetOpacity(1.0f);
622 }
623
624 void Decorator::OnHandleTwoLeavesBoundary( PropertyNotification& source)
625 {
626   mTextInputHandles.GetSelectionHandleTwo().SetOpacity(0.0f);
627 }
628
629 void Decorator::OnHandleTwoWithinBoundary(PropertyNotification& source)
630 {
631   mTextInputHandles.GetSelectionHandleTwo().SetOpacity(1.0f);
632 }
633
634 void Decorator::OnLeftBoundaryExceeded(PropertyNotification& source)
635 {
636   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnLeftBoundaryExceeded\n");
637   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
638   selectionHandleOne.SetScale( -1.0f, 1.0f, 1.0f );
639   selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_LEFT);
640 }
641
642 void Decorator::OnReturnToLeftBoundary(PropertyNotification& source)
643 {
644   DALI_LOG_INFO(gLogFilter, Debug::General, "TextInputDecorationLayouter::OnReturnToLeftBoundary\n");
645   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
646   selectionHandleOne.SetScale( 1.0f, 1.0f, 1.0f );
647   selectionHandleOne.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
648 }
649
650 void Decorator::OnRightBoundaryExceeded(PropertyNotification& source)
651 {
652   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
653   selectionHandleTwo.SetScale( -1.0f, 1.0f, 1.0f );
654   selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_RIGHT);
655 }
656
657 void Decorator::OnReturnToRightBoundary(PropertyNotification& source)
658 {
659   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
660   selectionHandleTwo.SetScale( 1.0f, 1.0f, 1.0f );
661   selectionHandleTwo.SetAnchorPoint( AnchorPoint::TOP_LEFT);
662 }
663
664 void Decorator::SetUpHandlePropertyNotifications()
665 {
666   /* Property notifications for handles exceeding the boundary and returning back within boundary */
667
668   Vector3 handlesize = GetSelectionHandleSize();
669
670   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
671   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
672
673   // Exceeding horizontal boundary
674   PropertyNotification leftNotification = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.x + handlesize.x) );
675   leftNotification.NotifySignal().Connect( this, &Decorator::OnLeftBoundaryExceeded );
676
677   PropertyNotification rightNotification = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.z - handlesize.x ) );
678   rightNotification.NotifySignal().Connect( this, &Decorator::OnRightBoundaryExceeded );
679
680   // Within horizontal boundary
681   PropertyNotification leftLeaveNotification = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_X, GreaterThanCondition( mBoundingRectangleWorldCoordinates.x + 2*handlesize.x ) );
682   leftLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToLeftBoundary );
683
684   PropertyNotification rightLeaveNotification = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_X, LessThanCondition( mBoundingRectangleWorldCoordinates.z - 2*handlesize.x ) );
685   rightLeaveNotification.NotifySignal().Connect( this, &Decorator::OnReturnToRightBoundary );
686
687   // Exceeding vertical boundary
688   PropertyNotification verticalExceedNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
689                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
690                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
691   verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneLeavesBoundary );
692
693   PropertyNotification verticalExceedNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
694                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
695                                                                          mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
696   verticalExceedNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoLeavesBoundary );
697
698   // Within vertical boundary
699   PropertyNotification verticalWithinNotificationOne = selectionHandleOne.AddPropertyNotification( Actor::WORLD_POSITION_Y,
700                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
701                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
702   verticalWithinNotificationOne.NotifySignal().Connect( this, &Decorator::OnHandleOneWithinBoundary );
703
704   PropertyNotification verticalWithinNotificationTwo = selectionHandleTwo.AddPropertyNotification( Actor::WORLD_POSITION_Y,
705                                                        InsideCondition( mBoundingRectangleWorldCoordinates.y + handlesize.y,
706                                                                         mBoundingRectangleWorldCoordinates.w - handlesize.y ) );
707   verticalWithinNotificationTwo.NotifySignal().Connect( this, &Decorator::OnHandleTwoWithinBoundary );
708 }
709
710 /**
711  * PopUp
712  */
713 Vector3 Decorator::PositionOfPopUpRelativeToSelectionHandles()
714 {
715   Vector3 position;
716   Vector2 min, max;
717   Vector3 topHandle;
718   Size rowSize;
719
720   // When text is selected, show popup above top handle (and text), or below bottom handle.
721
722   // topHandle: referring to the top most point of the handle or the top line of selection.
723   if ( mSelectionHandleTwoActualPosition.y > mSelectionHandleOneActualPosition.y ) // Handle may switch positions so calculate which is top.
724   {
725     topHandle = mSelectionHandleOneActualPosition;
726     rowSize= mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleOnePosition, min, max );
727   }
728   else
729   {
730     topHandle = mSelectionHandleTwoActualPosition;
731     rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( mSelectionHandleTwoPosition, min, max );
732   }
733   topHandle.y += TOP_HANDLE_TOP_OFFSET - rowSize.height;
734   position = Vector3(topHandle.x, topHandle.y, 0.0f);
735
736   return position;
737 }
738
739 Vector3 Decorator::AlternatePopUpPositionRelativeToSelectionHandles()
740 {
741   // alternativePosition: referring to the bottom most point of the handle or the bottom line of selection.
742   Vector3 alternativePosition;
743   alternativePosition.y = std::max ( mSelectionHandleTwoActualPosition.y , mSelectionHandleOneActualPosition.y );
744   alternativePosition.y += GetSelectionHandleSize().y + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET;
745
746   return alternativePosition;
747 }
748
749 Vector3 Decorator::PositionOfPopUpRelativeToCursor()
750 {
751   // When no text is selected, show PopUp at position of cursor
752   Vector3 position;
753   Vector2 min, max;
754   std::size_t cursorPosition = GetCurrentCursorPosition();
755   position = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
756   const Size rowSize = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorPosition, min, max );
757   position.y -= rowSize.height;
758
759   return position;
760 }
761
762 Vector3 Decorator::AlternatePopUpPositionRelativeToCursor()
763 {
764   std::size_t cursorPosition = GetCurrentCursorPosition();
765   Vector3 alternativePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorPosition );
766
767   if (  mTextInputHandles.GetGrabHandle() )
768   {
769     // If grab handle enabled then position pop-up below the grab handle.
770     alternativePosition.y +=  mTextInputHandles.GetGrabHandle().GetCurrentSize().height  + mPopUpPanel.GetSize().y + BOTTOM_HANDLE_BOTTOM_OFFSET ;
771   }
772   else
773   {
774     alternativePosition.y += mPopUpPanel.GetSize().y;
775   }
776
777   return alternativePosition;
778
779 }
780
781 Vector3 Decorator::PositionOfPopUpRelativeToGrabHandle()
782 {
783    return Vector3::ZERO;
784 }
785
786 void Decorator::ShowPopUp()
787 {
788   Vector3 position;
789   Vector3 alternativePosition;
790   Size rowSize;
791
792   DALI_ASSERT_DEBUG( mPopUpTarget && "PopUp Target Actor does not exist" );
793
794   if( mHighlightMeshActor ) // Text Selection mode
795   {
796     position = PositionOfPopUpRelativeToSelectionHandles();
797   }
798   else // Not in Text Selection mode so position relative to cursor.
799   {
800     position = PositionOfPopUpRelativeToCursor();
801   }
802
803   // reposition popup above the desired cursor position.
804   mPopUpPanel.Show( mPopUpTarget, true );
805   mPopUpPanel.Self().SetPosition( position );
806   mPopUpPanel.PressedSignal().Connect( this, &Decorator::OnPopupButtonPressed );
807
808   SetUpPopUpPositionNotifications();
809   mPopUpPanel.ApplyConfinementConstraint( mBoundingRectangleWorldCoordinates );
810 }
811
812 void Decorator::ShowPopUp( Actor target )
813 {
814   mPopUpTarget = target;
815   ShowPopupCutCopyPaste();
816 }
817
818 void Decorator::ShowPopupCutCopyPaste()
819 {
820   bool isAllTextSelectedAlready = ( mTextViewCharacterPositioning.StyledTextSize() == GetSelectedText().size() );
821   bool isTextEmpty = mTextViewCharacterPositioning.IsStyledTextEmpty() ;
822   bool isSubsetOfTextAlreadySelected = ( !isAllTextSelectedAlready ) && mHighlightMeshActor;
823
824   Clipboard clipboard = Clipboard::Get();
825   bool hasClipboardGotContent = clipboard.NumberOfItems();
826
827   mPopUpPanel.CreateCutCopyPastePopUp( isAllTextSelectedAlready, isTextEmpty, hasClipboardGotContent, isSubsetOfTextAlreadySelected );
828   ShowPopUp();
829 }
830
831 void Decorator::HidePopUp( bool animate, bool signalFinished )
832 {
833 }
834
835 void Decorator::AddPopupOption(const std::string& name, const std::string& caption, const Image icon, bool finalOption)
836 {
837   mPopUpPanel.AddButton(name, caption, icon, finalOption);
838 }
839
840 void Decorator::ClearPopup()
841 {
842   mPopUpPanel.Clear();
843 }
844
845 void Decorator::PopUpLeavesVerticalBoundary( PropertyNotification& source)
846 {
847   Vector3 position, alternativePosition;
848
849   if( mHighlightMeshActor ) // Text Selection mode
850   {
851     alternativePosition = AlternatePopUpPositionRelativeToSelectionHandles();
852   }
853   else // Not in Text Selection mode
854   {
855     alternativePosition = AlternatePopUpPositionRelativeToCursor();
856     // if can't be positioned above, then position below row.
857   }
858   // reposition popup above the desired cursor position.
859   mPopUpPanel.Self().SetPosition( alternativePosition );
860 }
861
862 void Decorator::SetUpPopUpPositionNotifications( )
863 {
864   // Note Property notifications ignore any set anchor point so conditions must allow for this.  Default is Top Left.
865
866   // Exceeding vertical boundary
867   PropertyNotification verticalExceedNotificationOne = mPopUpPanel.Self().AddPropertyNotification( Actor::WORLD_POSITION_Y,
868                                                        OutsideCondition( mBoundingRectangleWorldCoordinates.y + mPopUpPanel.GetSize().y/2,
869                                                                          mBoundingRectangleWorldCoordinates.w - mPopUpPanel.GetSize().y/2 ) );
870   verticalExceedNotificationOne.NotifySignal().Connect( this, &Decorator::PopUpLeavesVerticalBoundary );
871 }
872
873 bool Decorator::OnPopupButtonPressed( Toolkit::Button button )
874 {
875   mPopUpButtonPressedSignal.Emit( button );
876   return false;
877 }
878
879 Decorator::PressedSignal& Decorator::PopUpButtonPressedSignal()
880 {
881   return mPopUpButtonPressedSignal;
882 }
883
884 Decorator::CursorPositionedSignal& Decorator::CursorRePositionedSignal()
885 {
886   return mCursorRePositionedSignal;
887 }
888
889 /**
890  *  Decoration Positioning during Scrolling
891  */
892 void Decorator::TextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition )
893 {
894   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::TextViewScrolled\n");
895
896   const Vector3& controlSize = mTextViewCharacterPositioning.GetTextView().GetCurrentSize(); // todo Could store size and only update in Control Size change.
897   Size cursorSize( CURSOR_THICKNESS, 0.f );
898
899   // Updates the cursor and grab handle position and visibility.
900   if( mTextInputHandles.GetGrabHandle() || mCursor )
901   {
902     Vector2 min, max;
903     size_t cursorTextPosition = GetCurrentCursorPosition();
904     cursorSize.height  = mTextViewCharacterPositioning.GetRowRectFromCharacterPosition( cursorTextPosition, min, max ).height;
905
906     const Vector3 cursorPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( cursorTextPosition );
907
908     bool mIsCursorInScrollArea = IsPositionWithinControl( cursorPosition, cursorSize, controlSize );
909     bool mIsGrabHandleInScrollArea = mIsCursorInScrollArea;
910
911     Vector2 actualGrabHandlePosition = cursorPosition.GetVectorXY();
912
913     if( mTextInputHandles.GetGrabHandle() )
914     {
915       ShowGrabHandle( mGrabHandleVisibility && mIsGrabHandleInScrollArea );
916       PositionGrabHandle( cursorTextPosition );
917     }
918
919     if( mCursor )
920     {
921       mCursor.SetVisible( mCursorVisibility && mIsCursorInScrollArea );
922       DrawCursor( cursorTextPosition );
923       mCursor.SetPosition( Vector3(actualGrabHandlePosition) + UI_OFFSET );
924     }
925   }
926
927   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
928   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
929
930   // Updates the selection handles and highlighted text position and visibility.
931   if( mTextInputHandles.GetSelectionHandleOne() && mTextInputHandles.GetSelectionHandleTwo() )
932   {
933     const Vector3 cursorPositionOne = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition );
934     const Vector3 cursorPositionTwo = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition );
935
936     Size cursorSize( GetCursorSizeAt( mSelectionHandleOnePosition ) );
937     const bool isSelectionHandleOneVisible = IsPositionWithinControl( cursorPositionOne, cursorSize, controlSize );
938
939     cursorSize =  GetCursorSizeAt( mSelectionHandleTwoPosition );
940     const bool isSelectionHandleTwoVisible = IsPositionWithinControl( cursorPositionTwo, cursorSize, controlSize );
941
942     mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY();
943     mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY();
944
945     selectionHandleOne.SetVisible( isSelectionHandleOneVisible );
946     selectionHandleTwo.SetVisible( isSelectionHandleTwoVisible );
947
948     PositionSelectionHandle( selectionHandleOne, mSelectionHandleOneActualPosition, mSelectionHandleOnePosition );
949     PositionSelectionHandle( selectionHandleTwo, mSelectionHandleTwoActualPosition, mSelectionHandleTwoPosition );
950
951     if( mHighlightMeshActor )
952     {
953       mHighlightMeshActor.SetVisible( true );
954       ShowUpdatedHighlight();
955     }
956   }
957 }
958
959 void Decorator::StartScrollTimer()
960 {
961   if( !mScrollTimer )
962   {
963     mScrollTimer = Timer::New( SCROLL_TICK_INTERVAL );
964     mScrollTimer.TickSignal().Connect( this, &Decorator::OnScrollTimerTick );
965   }
966
967   if( !mScrollTimer.IsRunning() )
968   {
969     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StartScrollTimer\n");
970     mScrollTimer.Start();
971   }
972 }
973
974 void Decorator::StopScrollTimer()
975 {
976   if( mScrollTimer )
977   {
978     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::StopScrollTimer\n");
979
980     mScrollTimer.Stop();
981     mScrollTimer.Reset();
982   }
983 }
984
985 bool Decorator::OnScrollTimerTick()
986 {
987   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick\n");
988
989   if ( mGrabHandleVisibility && mTextInputHandles.GetGrabHandle() )
990   {
991     std::size_t newGrabHandlePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mActualGrabHandlePosition.GetVectorXY() );
992     if ( mGrabHandlePosition != newGrabHandlePosition )
993     {
994       Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
995       Vector2 scrollDelta = ( mActualGrabHandlePosition - mCurrentHandlePosition ).GetVectorXY();
996       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "TextInputDecorationLayouter::OnScrollTimerTick scrollPosition(%f) scrollDelta(%f)\n", scrollPosition.x, scrollDelta.x);
997       scrollPosition += scrollDelta;
998       mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
999
1000       mActualGrabHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newGrabHandlePosition ).GetVectorXY();
1001     }
1002   }
1003
1004   Actor selectionHandleOne = mTextInputHandles.GetSelectionHandleOne();
1005   Actor selectionHandleTwo = mTextInputHandles.GetSelectionHandleTwo();
1006
1007   if ( selectionHandleOne && selectionHandleTwo )
1008   {
1009     std::size_t newHandleOnePosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleOneActualPosition.GetVectorXY() );
1010
1011     // todo duplicated code should be a function
1012
1013     if ( mSelectionHandleOnePosition != newHandleOnePosition )
1014     {
1015       const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleOnePosition );
1016
1017       Vector2 scrollDelta = ( actualPosition - mSelectionHandleOneActualPosition ).GetVectorXY();
1018
1019       Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1020       scrollPosition += scrollDelta;
1021       mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1022
1023       mSelectionHandleOnePosition = newHandleOnePosition;
1024       mSelectionHandleOneActualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition ).GetVectorXY();
1025     }
1026     else
1027     {
1028       mSelectionHandleOneActualPosition.x += mScrollDisplacement.x;
1029       mSelectionHandleOneActualPosition.y += mScrollDisplacement.y;
1030     }
1031
1032     std::size_t newHandleTwoPosition = mTextViewCharacterPositioning.ReturnClosestIndex( mSelectionHandleTwoActualPosition.GetVectorXY() );
1033
1034     if ( mSelectionHandleTwoPosition != newHandleTwoPosition )
1035     {
1036       const Vector3 actualPosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( newHandleTwoPosition );
1037
1038       Vector2 scrollDelta = ( actualPosition - mSelectionHandleTwoActualPosition ).GetVectorXY();
1039
1040       Vector2 scrollPosition = mTextViewCharacterPositioning.GetScrollPosition();
1041       scrollPosition += scrollDelta;
1042       mTextViewCharacterPositioning.SetScrollPosition( scrollPosition );
1043
1044       mSelectionHandleTwoPosition = newHandleTwoPosition;
1045       mCurrentHandlePosition = mTextViewCharacterPositioning.GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition ).GetVectorXY();
1046
1047     }
1048     else
1049     {
1050       mSelectionHandleTwoActualPosition.x += mScrollDisplacement.x;
1051       mSelectionHandleTwoActualPosition.y += mScrollDisplacement.y;
1052     }
1053   }
1054
1055   return true;
1056 }
1057
1058 /**
1059  *  Text Selection
1060  */
1061 MarkupProcessor::StyledTextArray Decorator::GetSelectedText()
1062 {
1063   MarkupProcessor::StyledTextArray currentSelectedText;
1064
1065   if ( mHighlightMeshActor ) // Text Selected
1066   {
1067     MarkupProcessor::StyledTextArray::iterator it = mTextViewCharacterPositioning.GetStyledTextArray().begin() + std::min(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
1068     MarkupProcessor::StyledTextArray::iterator end = mTextViewCharacterPositioning.GetStyledTextArray().begin() + std::max(mSelectionHandleOnePosition, mSelectionHandleTwoPosition);
1069
1070     for(; it != end; ++it)
1071     {
1072       MarkupProcessor::StyledText& styledText( *it );
1073       currentSelectedText.push_back( styledText );
1074     }
1075   }
1076   return currentSelectedText;
1077 }
1078
1079 } // Internal
1080
1081 } // namespace Toolkit
1082
1083 } // namespace Dali
1084