System Clipboard shown and items can be pasted
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-impl.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/adaptor-framework/key.h>
23 #include <dali/integration-api/debug.h>
24
25 // INTERNAL INCLUDES
26 #include <dali-toolkit/internal/text/bidirectional-support.h>
27 #include <dali-toolkit/internal/text/character-set-conversion.h>
28 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
29 #include <dali-toolkit/internal/text/multi-language-support.h>
30 #include <dali-toolkit/internal/text/script-run.h>
31 #include <dali-toolkit/internal/text/segmentation.h>
32 #include <dali-toolkit/internal/text/shaper.h>
33 #include <dali-toolkit/internal/text/text-io.h>
34 #include <dali-toolkit/internal/text/text-view.h>
35
36 namespace
37 {
38
39 #if defined(DEBUG_ENABLED)
40   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_CONTROLS");
41 #endif
42
43 /**
44  * @brief Some characters can be shaped in more than one glyph.
45  * This struct is used to retrieve metrics from these group of glyphs.
46  */
47 struct GlyphMetrics
48 {
49   GlyphMetrics()
50   : fontHeight( 0.f ),
51     advance( 0.f ),
52     ascender( 0.f ),
53     xBearing( 0.f )
54   {}
55
56   ~GlyphMetrics()
57   {}
58
59   float fontHeight; ///< The font's height of that glyphs.
60   float advance;    ///< The sum of all the advances of all the glyphs.
61   float ascender;   ///< The font's ascender.
62   float xBearing;   ///< The x bearing of the first glyph.
63 };
64
65 const std::string EMPTY_STRING("");
66
67 } // namespace
68
69 namespace Dali
70 {
71
72 namespace Toolkit
73 {
74
75 namespace Text
76 {
77
78 /**
79  * @brief Get some glyph's metrics of a group of glyphs formed as a result of shaping one character.
80  *
81  * @param[in] glyphIndex The index to the first glyph.
82  * @param[in] numberOfGlyphs The number of glyphs.
83  * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
84  * @param[in]
85  * @param[in]
86  */
87 void GetGlyphsMetrics( GlyphIndex glyphIndex,
88                        Length numberOfGlyphs,
89                        GlyphMetrics& glyphMetrics,
90                        VisualModelPtr visualModel,
91                        TextAbstraction::FontClient& fontClient )
92 {
93   const GlyphInfo* glyphsBuffer = visualModel->mGlyphs.Begin();
94
95   const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
96
97   Text::FontMetrics fontMetrics;
98   fontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
99
100   glyphMetrics.fontHeight = fontMetrics.height;
101   glyphMetrics.advance = firstGlyph.advance;
102   glyphMetrics.ascender = fontMetrics.ascender;
103   glyphMetrics.xBearing = firstGlyph.xBearing;
104
105   for( unsigned int i = 1u; i < numberOfGlyphs; ++i )
106   {
107     const GlyphInfo& glyphInfo = *( glyphsBuffer + glyphIndex + i );
108
109     glyphMetrics.advance += glyphInfo.advance;
110   }
111 }
112
113 EventData::EventData( DecoratorPtr decorator )
114 : mDecorator( decorator ),
115   mPlaceholderTextActive(),
116   mPlaceholderTextInactive(),
117   mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ),
118   mEventQueue(),
119   mScrollPosition(),
120   mState( INACTIVE ),
121   mPrimaryCursorPosition( 0u ),
122   mLeftSelectionPosition( 0u ),
123   mRightSelectionPosition( 0u ),
124   mPreEditStartPosition( 0u ),
125   mPreEditLength( 0u ),
126   mIsShowingPlaceholderText( false ),
127   mPreEditFlag( false ),
128   mDecoratorUpdated( false ),
129   mCursorBlinkEnabled( true ),
130   mGrabHandleEnabled( true ),
131   mGrabHandlePopupEnabled( true ),
132   mSelectionEnabled( true ),
133   mHorizontalScrollingEnabled( true ),
134   mVerticalScrollingEnabled( false ),
135   mUpdateCursorPosition( false ),
136   mUpdateLeftSelectionPosition( false ),
137   mUpdateRightSelectionPosition( false ),
138   mScrollAfterUpdatePosition( false ),
139   mScrollAfterDelete( false )
140 {}
141
142 EventData::~EventData()
143 {}
144
145 bool Controller::Impl::ProcessInputEvents()
146 {
147   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" );
148   if( NULL == mEventData )
149   {
150     // Nothing to do if there is no text input.
151     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" );
152     return false;
153   }
154
155   if( mEventData->mDecorator )
156   {
157     for( std::vector<Event>::iterator iter = mEventData->mEventQueue.begin();
158          iter != mEventData->mEventQueue.end();
159          ++iter )
160     {
161       switch( iter->type )
162       {
163         case Event::CURSOR_KEY_EVENT:
164         {
165           OnCursorKeyEvent( *iter );
166           break;
167         }
168         case Event::TAP_EVENT:
169         {
170           OnTapEvent( *iter );
171           break;
172         }
173         case Event::PAN_EVENT:
174         {
175           OnPanEvent( *iter );
176           break;
177         }
178         case Event::GRAB_HANDLE_EVENT:
179         case Event::LEFT_SELECTION_HANDLE_EVENT:
180         case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
181         {
182           OnHandleEvent( *iter );
183           break;
184         }
185         case Event::SELECT:
186         {
187           OnSelectEvent( *iter );
188           break;
189         }
190         case Event::SELECT_ALL:
191         {
192           OnSelectAllEvent();
193           break;
194         }
195       }
196     }
197   }
198
199   // The cursor must also be repositioned after inserts into the model
200   if( mEventData->mUpdateCursorPosition )
201   {
202     // Updates the cursor position and scrolls the text to make it visible.
203
204     UpdateCursorPosition();
205
206     if( mEventData->mScrollAfterUpdatePosition )
207     {
208       const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
209
210       ScrollToMakePositionVisible( primaryCursorPosition );
211       mEventData->mScrollAfterUpdatePosition = false;
212     }
213
214     mEventData->mDecoratorUpdated = true;
215     mEventData->mUpdateCursorPosition = false;
216   }
217   else if( mEventData->mScrollAfterDelete )
218   {
219     ScrollTextToMatchCursor();
220     mEventData->mDecoratorUpdated = true;
221     mEventData->mScrollAfterDelete = false;
222   }
223   else
224   {
225     bool leftScroll = false;
226     bool rightScroll = false;
227
228     if( mEventData->mUpdateLeftSelectionPosition )
229     {
230       UpdateSelectionHandle( LEFT_SELECTION_HANDLE );
231
232       if( mEventData->mScrollAfterUpdatePosition )
233       {
234         const Vector2& leftHandlePosition = mEventData->mDecorator->GetPosition( LEFT_SELECTION_HANDLE );
235
236         ScrollToMakePositionVisible( leftHandlePosition );
237         leftScroll = true;
238       }
239
240       mEventData->mDecoratorUpdated = true;
241       mEventData->mUpdateLeftSelectionPosition = false;
242     }
243
244     if( mEventData->mUpdateRightSelectionPosition )
245     {
246       UpdateSelectionHandle( RIGHT_SELECTION_HANDLE );
247
248       if( mEventData->mScrollAfterUpdatePosition )
249       {
250         const Vector2& rightHandlePosition = mEventData->mDecorator->GetPosition( RIGHT_SELECTION_HANDLE );
251
252         ScrollToMakePositionVisible( rightHandlePosition );
253         rightScroll = true;
254       }
255
256       mEventData->mDecoratorUpdated = true;
257       mEventData->mUpdateRightSelectionPosition = false;
258     }
259
260     if( leftScroll || rightScroll )
261     {
262       mEventData->mScrollAfterUpdatePosition = false;
263     }
264   }
265
266   mEventData->mEventQueue.clear();
267
268   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
269
270   const bool decoratorUpdated = mEventData->mDecoratorUpdated;
271   mEventData->mDecoratorUpdated = false;
272
273   return decoratorUpdated;
274 }
275
276 void Controller::Impl::UpdateModel( OperationsMask operationsRequired )
277 {
278   // Calculate the operations to be done.
279   const OperationsMask operations = static_cast<OperationsMask>( mOperationsPending & operationsRequired );
280
281   Vector<Character>& utf32Characters = mLogicalModel->mText;
282
283   const Length numberOfCharacters = utf32Characters.Count();
284
285   Vector<LineBreakInfo>& lineBreakInfo = mLogicalModel->mLineBreakInfo;
286   if( GET_LINE_BREAKS & operations )
287   {
288     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
289     // calculate the bidirectional info for each 'paragraph'.
290     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
291     // is not shaped together).
292     lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
293
294     SetLineBreakInfo( utf32Characters,
295                       lineBreakInfo );
296   }
297
298   Vector<WordBreakInfo>& wordBreakInfo = mLogicalModel->mWordBreakInfo;
299   if( GET_WORD_BREAKS & operations )
300   {
301     // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
302     wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
303
304     SetWordBreakInfo( utf32Characters,
305                       wordBreakInfo );
306   }
307
308   const bool getScripts = GET_SCRIPTS & operations;
309   const bool validateFonts = VALIDATE_FONTS & operations;
310
311   Vector<ScriptRun>& scripts = mLogicalModel->mScriptRuns;
312   Vector<FontRun>& validFonts = mLogicalModel->mFontRuns;
313
314   if( getScripts || validateFonts )
315   {
316     // Validates the fonts assigned by the application or assigns default ones.
317     // It makes sure all the characters are going to be rendered by the correct font.
318     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
319
320     if( getScripts )
321     {
322       // Retrieves the scripts used in the text.
323       multilanguageSupport.SetScripts( utf32Characters,
324                                        lineBreakInfo,
325                                        scripts );
326     }
327
328     if( validateFonts )
329     {
330       if( 0u == validFonts.Count() )
331       {
332         // Copy the requested font defaults received via the property system.
333         // These may not be valid i.e. may not contain glyphs for the necessary scripts.
334         GetDefaultFonts( validFonts, numberOfCharacters );
335       }
336
337       // Validates the fonts. If there is a character with no assigned font it sets a default one.
338       // After this call, fonts are validated.
339       multilanguageSupport.ValidateFonts( utf32Characters,
340                                           scripts,
341                                           validFonts );
342     }
343   }
344
345   Vector<Character> mirroredUtf32Characters;
346   bool textMirrored = false;
347   if( BIDI_INFO & operations )
348   {
349     // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
350     // bidirectional info.
351
352     Length numberOfParagraphs = 0u;
353
354     const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
355     for( Length index = 0u; index < numberOfCharacters; ++index )
356     {
357       if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
358       {
359         ++numberOfParagraphs;
360       }
361     }
362
363     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo;
364     bidirectionalInfo.Reserve( numberOfParagraphs );
365
366     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
367     SetBidirectionalInfo( utf32Characters,
368                           scripts,
369                           lineBreakInfo,
370                           bidirectionalInfo );
371
372     if( 0u != bidirectionalInfo.Count() )
373     {
374       // This paragraph has right to left text. Some characters may need to be mirrored.
375       // TODO: consider if the mirrored string can be stored as well.
376
377       textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
378
379       // Only set the character directions if there is right to left characters.
380       Vector<CharacterDirection>& directions = mLogicalModel->mCharacterDirections;
381       directions.Resize( numberOfCharacters );
382
383       GetCharactersDirection( bidirectionalInfo,
384                               directions );
385     }
386     else
387     {
388       // There is no right to left characters. Clear the directions vector.
389       mLogicalModel->mCharacterDirections.Clear();
390     }
391
392    }
393
394   Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
395   Vector<CharacterIndex>& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters;
396   Vector<Length>& charactersPerGlyph = mVisualModel->mCharactersPerGlyph;
397   if( SHAPE_TEXT & operations )
398   {
399     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
400     // Shapes the text.
401     ShapeText( textToShape,
402                lineBreakInfo,
403                scripts,
404                validFonts,
405                glyphs,
406                glyphsToCharactersMap,
407                charactersPerGlyph );
408
409     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
410     mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
411     mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
412   }
413
414   const Length numberOfGlyphs = glyphs.Count();
415
416   if( GET_GLYPH_METRICS & operations )
417   {
418     mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
419   }
420 }
421
422 void Controller::Impl::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
423 {
424   if( mFontDefaults )
425   {
426     FontRun fontRun;
427     fontRun.characterRun.characterIndex = 0;
428     fontRun.characterRun.numberOfCharacters = numberOfCharacters;
429     fontRun.fontId = mFontDefaults->GetFontId( mFontClient );
430     fontRun.isDefault = true;
431
432     fonts.PushBack( fontRun );
433   }
434 }
435
436 void Controller::Impl::OnCursorKeyEvent( const Event& event )
437 {
438   if( NULL == mEventData )
439   {
440     // Nothing to do if there is no text input.
441     return;
442   }
443
444   int keyCode = event.p1.mInt;
445
446   if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
447   {
448     if( mEventData->mPrimaryCursorPosition > 0u )
449     {
450       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u );
451     }
452   }
453   else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
454   {
455     if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition )
456     {
457       mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition );
458     }
459   }
460   else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
461   {
462     // TODO
463   }
464   else if(   Dali::DALI_KEY_CURSOR_DOWN == keyCode )
465   {
466     // TODO
467   }
468
469   mEventData->mUpdateCursorPosition = true;
470   mEventData->mScrollAfterUpdatePosition = true;
471 }
472
473 void Controller::Impl::OnTapEvent( const Event& event )
474 {
475   if( NULL != mEventData )
476   {
477     const unsigned int tapCount = event.p1.mUint;
478
479     if( 1u == tapCount )
480     {
481       if( ! IsShowingPlaceholderText() )
482       {
483         const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
484         const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
485
486         mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
487                                                                     yPosition );
488       }
489       else
490       {
491         mEventData->mPrimaryCursorPosition = 0u;
492       }
493
494       mEventData->mUpdateCursorPosition = true;
495       mEventData->mScrollAfterUpdatePosition = true;
496     }
497   }
498 }
499
500 void Controller::Impl::OnPanEvent( const Event& event )
501 {
502   if( NULL == mEventData )
503   {
504     // Nothing to do if there is no text input.
505     return;
506   }
507
508   int state = event.p1.mInt;
509
510   if( Gesture::Started    == state ||
511       Gesture::Continuing == state )
512   {
513     const Vector2& actualSize = mVisualModel->GetActualSize();
514     const Vector2 currentScroll = mEventData->mScrollPosition;
515
516     if( mEventData->mHorizontalScrollingEnabled )
517     {
518       const float displacementX = event.p2.mFloat;
519       mEventData->mScrollPosition.x += displacementX;
520
521       ClampHorizontalScroll( actualSize );
522     }
523
524     if( mEventData->mVerticalScrollingEnabled )
525     {
526       const float displacementY = event.p3.mFloat;
527       mEventData->mScrollPosition.y += displacementY;
528
529       ClampVerticalScroll( actualSize );
530     }
531
532     if( mEventData->mDecorator )
533     {
534       mEventData->mDecorator->UpdatePositions( mEventData->mScrollPosition - currentScroll );
535     }
536   }
537 }
538
539 void Controller::Impl::OnHandleEvent( const Event& event )
540 {
541   if( NULL == mEventData )
542   {
543     // Nothing to do if there is no text input.
544     return;
545   }
546
547   const unsigned int state = event.p1.mUint;
548   const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
549
550   if( HANDLE_PRESSED == state )
551   {
552     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
553     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
554     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
555
556     const CharacterIndex handleNewPosition = GetClosestCursorIndex( xPosition, yPosition );
557
558     if( Event::GRAB_HANDLE_EVENT == event.type )
559     {
560       ChangeState ( EventData::GRAB_HANDLE_PANNING );
561
562       if( handleNewPosition != mEventData->mPrimaryCursorPosition )
563       {
564         mEventData->mPrimaryCursorPosition = handleNewPosition;
565         mEventData->mUpdateCursorPosition = true;
566       }
567     }
568     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
569     {
570       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
571
572       if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) &&
573           ( handleNewPosition != mEventData->mRightSelectionPosition ) )
574       {
575         mEventData->mLeftSelectionPosition = handleNewPosition;
576
577         RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
578                                     mEventData->mRightSelectionPosition );
579
580         mEventData->mUpdateLeftSelectionPosition = true;
581       }
582     }
583     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
584     {
585       ChangeState ( EventData::SELECTION_HANDLE_PANNING );
586
587       if( ( handleNewPosition != mEventData->mRightSelectionPosition ) &&
588           ( handleNewPosition != mEventData->mLeftSelectionPosition ) )
589       {
590         mEventData->mRightSelectionPosition = handleNewPosition;
591
592         RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
593                                     mEventData->mRightSelectionPosition );
594
595         mEventData->mUpdateRightSelectionPosition = true;
596       }
597     }
598   } // end ( HANDLE_PRESSED == state )
599   else if( ( HANDLE_RELEASED == state ) ||
600            handleStopScrolling )
601   {
602     CharacterIndex handlePosition = 0u;
603     if( handleStopScrolling )
604     {
605       // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
606       const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
607       const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
608
609       handlePosition = GetClosestCursorIndex( xPosition, yPosition );
610     }
611
612     if( Event::GRAB_HANDLE_EVENT == event.type )
613     {
614       mEventData->mUpdateCursorPosition = true;
615
616       ChangeState( EventData::EDITING_WITH_POPUP );
617
618       if( handleStopScrolling )
619       {
620         mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
621         mEventData->mPrimaryCursorPosition = handlePosition;
622       }
623     }
624     else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
625     {
626       ChangeState( EventData::SELECTING );
627
628       if( handleStopScrolling )
629       {
630         mEventData->mUpdateLeftSelectionPosition = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition);
631         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
632
633         if( mEventData->mUpdateLeftSelectionPosition )
634         {
635           mEventData->mLeftSelectionPosition = handlePosition;
636
637           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
638                                       mEventData->mRightSelectionPosition );
639         }
640       }
641     }
642     else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
643     {
644       ChangeState( EventData::SELECTING );
645
646       if( handleStopScrolling )
647       {
648         mEventData->mUpdateRightSelectionPosition = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition );
649         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
650
651         if( mEventData->mUpdateRightSelectionPosition )
652         {
653           mEventData->mRightSelectionPosition = handlePosition;
654           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
655                                       mEventData->mRightSelectionPosition );
656         }
657       }
658     }
659
660     mEventData->mDecoratorUpdated = true;
661   } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
662   else if( HANDLE_SCROLLING == state )
663   {
664     const float xSpeed = event.p2.mFloat;
665     const Vector2& actualSize = mVisualModel->GetActualSize();
666     const Vector2 currentScrollPosition = mEventData->mScrollPosition;
667
668     mEventData->mScrollPosition.x += xSpeed;
669
670     ClampHorizontalScroll( actualSize );
671
672     if( Vector2::ZERO == ( currentScrollPosition - mEventData->mScrollPosition ) )
673     {
674       // Notify the decorator there is no more text to scroll.
675       // The decorator won't send more scroll events.
676       mEventData->mDecorator->NotifyEndOfScroll();
677     }
678     else
679     {
680       const bool scrollRightDirection = xSpeed > 0.f;
681       const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
682       const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
683
684       if( Event::GRAB_HANDLE_EVENT == event.type )
685       {
686         ChangeState( EventData::GRAB_HANDLE_PANNING );
687
688         Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE );
689
690         // Position the grag handle close to either the left or right edge.
691         position.x = scrollRightDirection ? 0.f : mControlSize.width;
692
693         // Get the new handle position.
694         // The grab handle's position is in decorator coords. Need to transforms to text coords.
695         const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
696                                                                      position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
697
698         mEventData->mUpdateCursorPosition = mEventData->mPrimaryCursorPosition != handlePosition;
699         mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateCursorPosition;
700         mEventData->mPrimaryCursorPosition = handlePosition;
701       }
702       else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
703       {
704         // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
705         //       Think if something can be done to save power.
706
707         ChangeState( EventData::SELECTION_HANDLE_PANNING );
708
709         Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
710
711         // Position the selection handle close to either the left or right edge.
712         position.x = scrollRightDirection ? 0.f : mControlSize.width;
713
714         // Get the new handle position.
715         // The selection handle's position is in decorator coords. Need to transforms to text coords.
716         const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
717                                                                      position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
718
719         if( leftSelectionHandleEvent )
720         {
721           mEventData->mUpdateLeftSelectionPosition = handlePosition != mEventData->mLeftSelectionPosition;
722           mEventData->mLeftSelectionPosition = handlePosition;
723         }
724         else
725         {
726           mEventData->mUpdateRightSelectionPosition = handlePosition != mEventData->mRightSelectionPosition;
727           mEventData->mRightSelectionPosition = handlePosition;
728         }
729
730         if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
731         {
732           RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
733                                       mEventData->mRightSelectionPosition );
734
735           mEventData->mScrollAfterUpdatePosition = true;
736         }
737       }
738       mEventData->mDecoratorUpdated = true;
739     }
740   } // end ( HANDLE_SCROLLING == state )
741 }
742
743 void Controller::Impl::OnSelectEvent( const Event& event )
744 {
745   if( NULL == mEventData )
746   {
747     // Nothing to do if there is no text.
748     return;
749   }
750
751   if( mEventData->mSelectionEnabled )
752   {
753     // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
754     const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
755     const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
756
757     const CharacterIndex leftPosition = mEventData->mLeftSelectionPosition;
758     const CharacterIndex rightPosition = mEventData->mRightSelectionPosition;
759
760     RepositionSelectionHandles( xPosition,
761                                 yPosition );
762
763     mEventData->mUpdateLeftSelectionPosition = leftPosition != mEventData->mLeftSelectionPosition;
764     mEventData->mUpdateRightSelectionPosition = rightPosition != mEventData->mRightSelectionPosition;
765
766     mEventData->mScrollAfterUpdatePosition = ( ( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition ) &&
767                                                ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition ) );
768   }
769 }
770
771 void Controller::Impl::OnSelectAllEvent()
772 {
773   if( NULL == mEventData )
774   {
775     // Nothing to do if there is no text.
776     return;
777   }
778
779   if( mEventData->mSelectionEnabled )
780   {
781     RepositionSelectionHandles( 0u,
782                                 mLogicalModel->mText.Count() );
783
784     mEventData->mScrollAfterUpdatePosition = true;
785     mEventData->mUpdateLeftSelectionPosition = true;
786     mEventData->mUpdateRightSelectionPosition = true;
787   }
788 }
789
790 void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetreival )
791 {
792   if( mEventData->mLeftSelectionPosition ==  mEventData->mRightSelectionPosition )
793   {
794     // Nothing to select if handles are in the same place.
795     selectedText="";
796     return;
797   }
798
799   //Get start and end position of selection
800   uint32_t startOfSelectedText = mEventData->mLeftSelectionPosition;
801   uint32_t lengthOfSelectedText =  mEventData->mRightSelectionPosition - startOfSelectedText;
802
803   // Validate the start and end selection points
804   if( ( startOfSelectedText >= 0 ) && (  ( startOfSelectedText + lengthOfSelectedText ) <=  mLogicalModel->mText.Count() ) )
805   {
806     //Get text as a UTF8 string
807     Vector<Character>& utf32Characters = mLogicalModel->mText;
808
809     Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText );
810
811     if ( deleteAfterRetreival  ) // Only delete text if copied successfully
812     {
813       // Delete text between handles
814       Vector<Character>& currentText = mLogicalModel->mText;
815
816       Vector<Character>::Iterator first = currentText.Begin() + startOfSelectedText;
817       Vector<Character>::Iterator last  = first + lengthOfSelectedText;
818       currentText.Erase( first, last );
819     }
820     mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition;
821     mEventData->mScrollAfterDelete = true;
822     mEventData->mDecoratorUpdated = true;
823   }
824 }
825
826 void Controller::Impl::ShowClipboard()
827 {
828   if ( mClipboard )
829   {
830     mClipboard.ShowClipboard();
831   }
832 }
833
834 void Controller::Impl::HideClipboard()
835 {
836   if ( mClipboard )
837   {
838     mClipboard.HideClipboard();
839   }
840 }
841
842 bool Controller::Impl::CopyStringToClipboard( std::string& source )
843 {
844   //Send string to clipboard
845   return ( mClipboard && mClipboard.SetItem( source ) );
846 }
847
848 void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending )
849 {
850   std::string selectedText;
851   RetrieveSelection( selectedText, deleteAfterSending );
852   CopyStringToClipboard( selectedText );
853   ChangeState( EventData::EDITING );
854 }
855
856 void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retreivedString )
857 {
858   if ( mClipboard )
859   {
860     retreivedString =  mClipboard.GetItem( itemIndex );
861   }
862 }
863
864 void Controller::Impl::RepositionSelectionHandles( CharacterIndex selectionStart, CharacterIndex selectionEnd )
865 {
866   if( selectionStart == selectionEnd )
867   {
868     // Nothing to select if handles are in the same place.
869     return;
870   }
871
872   mEventData->mDecorator->ClearHighlights();
873
874   mEventData->mLeftSelectionPosition = selectionStart;
875   mEventData->mRightSelectionPosition = selectionEnd;
876
877   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
878   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
879   const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
880   const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
881
882   // TODO: Better algorithm to create the highlight box.
883   // TODO: Multi-line.
884
885   const Vector<LineRun>& lines = mVisualModel->mLines;
886   const LineRun& firstLine = *lines.Begin();
887   const float height = firstLine.ascender + -firstLine.descender;
888
889   const bool indicesSwapped = ( selectionStart > selectionEnd );
890   if( indicesSwapped )
891   {
892     std::swap( selectionStart, selectionEnd );
893   }
894
895   GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
896   GlyphIndex glyphEnd = *( charactersToGlyphBuffer + ( selectionEnd - 1u ) ) + *( glyphsPerCharacterBuffer + ( selectionEnd - 1u ) ) - 1u;
897
898   mEventData->mDecorator->SwapSelectionHandlesEnabled( firstLine.direction != indicesSwapped );
899
900   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
901
902   for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
903   {
904     const GlyphInfo& glyph = *( glyphsBuffer + index );
905     const Vector2& position = *( positionsBuffer + index );
906
907     const float xPosition = position.x - glyph.xBearing + offset.x;
908     mEventData->mDecorator->AddHighlight( xPosition, offset.y, xPosition + glyph.advance, height );
909   }
910
911   CursorInfo primaryCursorInfo;
912   GetCursorPosition( mEventData->mLeftSelectionPosition,
913                      primaryCursorInfo );
914
915   CursorInfo secondaryCursorInfo;
916   GetCursorPosition( mEventData->mRightSelectionPosition,
917                      secondaryCursorInfo );
918
919   const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
920   const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
921
922   mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryPosition.x, primaryPosition.y, primaryCursorInfo.lineHeight );
923
924   mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, secondaryCursorInfo.lineHeight );
925
926   // Set the flag to update the decorator.
927   mEventData->mDecoratorUpdated = true;
928 }
929
930 void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
931 {
932   if( NULL == mEventData )
933   {
934     // Nothing to do if there is no text input.
935     return;
936   }
937
938   if( IsShowingPlaceholderText() )
939   {
940     // Nothing to do if there is the place-holder text.
941     return;
942   }
943
944   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
945   const Length numberOfLines  = mVisualModel->mLines.Count();
946   if( 0 == numberOfGlyphs ||
947       0 == numberOfLines )
948   {
949     // Nothing to do if there is no text.
950     return;
951   }
952
953   // Find which word was selected
954   CharacterIndex selectionStart( 0 );
955   CharacterIndex selectionEnd( 0 );
956   FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
957   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
958
959   if( selectionStart == selectionEnd )
960   {
961     ChangeState( EventData::EDITING );
962     // Nothing to select. i.e. a white space, out of bounds
963     return;
964   }
965
966   RepositionSelectionHandles( selectionStart, selectionEnd );
967 }
968
969 void Controller::Impl::ChangeState( EventData::State newState )
970 {
971   if( NULL == mEventData )
972   {
973     // Nothing to do if there is no text input.
974     return;
975   }
976
977   if( mEventData->mState != newState )
978   {
979     mEventData->mState = newState;
980
981     if( EventData::INACTIVE == mEventData->mState )
982     {
983       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
984       mEventData->mDecorator->StopCursorBlink();
985       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
986       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
987       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
988       mEventData->mDecorator->SetPopupActive( false );
989       mEventData->mDecoratorUpdated = true;
990       HideClipboard();
991     }
992     else if ( EventData::SELECTING == mEventData->mState )
993     {
994       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
995       mEventData->mDecorator->StopCursorBlink();
996       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
997       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
998       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
999       if( mEventData->mGrabHandlePopupEnabled )
1000       {
1001         TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1002         if ( !IsClipboardEmpty() )
1003         {
1004           buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1005           buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1006         }
1007
1008         mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1009         mEventData->mDecorator->SetPopupActive( true );
1010       }
1011       mEventData->mDecoratorUpdated = true;
1012     }
1013     else if ( EventData::SELECTION_CHANGED  == mEventData->mState )
1014     {
1015       if( mEventData->mGrabHandlePopupEnabled )
1016       {
1017         TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::Buttons(  TextSelectionPopup::CUT | TextSelectionPopup::COPY );
1018         if (  !IsClipboardEmpty() )
1019         {
1020           buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1021           buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1022         }
1023         mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1024         mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1025         mEventData->mDecorator->SetPopupActive( true );
1026       }
1027       mEventData->mDecoratorUpdated = true;
1028     }
1029     else if( EventData::EDITING == mEventData->mState )
1030     {
1031       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1032       if( mEventData->mCursorBlinkEnabled )
1033       {
1034         mEventData->mDecorator->StartCursorBlink();
1035       }
1036       // Grab handle is not shown until a tap is received whilst EDITING
1037       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1038       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1039       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1040       if( mEventData->mGrabHandlePopupEnabled )
1041       {
1042         mEventData->mDecorator->SetPopupActive( false );
1043       }
1044       mEventData->mDecoratorUpdated = true;
1045       HideClipboard();
1046     }
1047     else if( EventData::EDITING_WITH_POPUP == mEventData->mState )
1048     {
1049       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1050       if( mEventData->mCursorBlinkEnabled )
1051       {
1052         mEventData->mDecorator->StartCursorBlink();
1053       }
1054       if( mEventData->mSelectionEnabled )
1055       {
1056         mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1057         mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1058       }
1059       else
1060       {
1061         mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1062       }
1063       if( mEventData->mGrabHandlePopupEnabled )
1064       {
1065         TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL );
1066
1067         if ( !IsClipboardEmpty() )
1068         {
1069           buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) );
1070           buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) );
1071         }
1072
1073         mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow );
1074         mEventData->mDecorator->SetPopupActive( true );
1075       }
1076       HideClipboard();
1077       mEventData->mDecoratorUpdated = true;
1078     }
1079     else if ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState )
1080     {
1081       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
1082       mEventData->mDecorator->StopCursorBlink();
1083       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false );
1084       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true );
1085       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true );
1086       if( mEventData->mGrabHandlePopupEnabled )
1087       {
1088         mEventData->mDecorator->SetPopupActive( false );
1089       }
1090       mEventData->mDecoratorUpdated = true;
1091     }
1092     else if ( EventData::GRAB_HANDLE_PANNING == mEventData->mState )
1093     {
1094       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1095       if( mEventData->mCursorBlinkEnabled )
1096       {
1097         mEventData->mDecorator->StartCursorBlink();
1098       }
1099       mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true );
1100       mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false );
1101       mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false );
1102       if( mEventData->mGrabHandlePopupEnabled )
1103       {
1104         mEventData->mDecorator->SetPopupActive( false );
1105       }
1106       mEventData->mDecoratorUpdated = true;
1107     }
1108   }
1109 }
1110
1111 LineIndex Controller::Impl::GetClosestLine( float y ) const
1112 {
1113   float totalHeight = 0.f;
1114   LineIndex lineIndex = 0u;
1115
1116   const Vector<LineRun>& lines = mVisualModel->mLines;
1117   for( LineIndex endLine = lines.Count();
1118        lineIndex < endLine;
1119        ++lineIndex )
1120   {
1121     const LineRun& lineRun = lines[lineIndex];
1122     totalHeight += lineRun.ascender + -lineRun.descender;
1123     if( y < totalHeight )
1124     {
1125       return lineIndex;
1126     }
1127   }
1128
1129   if( lineIndex == 0 )
1130   {
1131     return 0;
1132   }
1133
1134   return lineIndex-1;
1135 }
1136
1137 void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
1138 {
1139   CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
1140   if( hitCharacter >= mLogicalModel->mText.Count() )
1141   {
1142     // Selection out of bounds.
1143     return;
1144   }
1145
1146   startIndex = hitCharacter;
1147   endIndex = hitCharacter;
1148
1149   if( !TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] ) )
1150   {
1151     // Find the start and end of the text
1152     for( startIndex = hitCharacter; startIndex > 0; --startIndex )
1153     {
1154       Character charCode = mLogicalModel->mText[ startIndex-1 ];
1155       if( TextAbstraction::IsWhiteSpace( charCode ) )
1156       {
1157         break;
1158       }
1159     }
1160     const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
1161     for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
1162     {
1163       Character charCode = mLogicalModel->mText[ endIndex ];
1164       if( TextAbstraction::IsWhiteSpace( charCode ) )
1165       {
1166         break;
1167       }
1168     }
1169   }
1170 }
1171
1172 CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
1173                                                         float visualY )
1174 {
1175   if( NULL == mEventData )
1176   {
1177     // Nothing to do if there is no text input.
1178     return 0u;
1179   }
1180
1181   CharacterIndex logicalIndex = 0u;
1182
1183   const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
1184   const Length numberOfLines  = mVisualModel->mLines.Count();
1185   if( 0 == numberOfGlyphs ||
1186       0 == numberOfLines )
1187   {
1188     return logicalIndex;
1189   }
1190
1191   // Find which line is closest
1192   const LineIndex lineIndex = GetClosestLine( visualY );
1193   const LineRun& line = mVisualModel->mLines[lineIndex];
1194
1195   // Get the positions of the glyphs.
1196   const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
1197   const Vector2* const positionsBuffer = positions.Begin();
1198
1199   // Get the visual to logical conversion tables.
1200   const CharacterIndex* const visualToLogicalBuffer = ( 0u != mLogicalModel->mVisualToLogicalMap.Count() ) ? mLogicalModel->mVisualToLogicalMap.Begin() : NULL;
1201   const CharacterIndex* const visualToLogicalCursorBuffer = mLogicalModel->mVisualToLogicalCursorMap.Begin();
1202
1203   // Get the character to glyph conversion table.
1204   const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1205
1206   // Get the glyphs per character table.
1207   const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
1208
1209   // If the vector is void, there is no right to left characters.
1210   const bool hasRightToLeftCharacters = NULL != visualToLogicalBuffer;
1211
1212   const CharacterIndex startCharacter = line.characterRun.characterIndex;
1213   const CharacterIndex endCharacter   = line.characterRun.characterIndex + line.characterRun.numberOfCharacters;
1214   DALI_ASSERT_DEBUG( endCharacter <= mLogicalModel->mText.Count() && "Invalid line info" );
1215
1216   // Whether there is a hit on a glyph.
1217   bool matched = false;
1218
1219   // Traverses glyphs in visual order. To do that use the visual to logical conversion table.
1220   CharacterIndex visualIndex = startCharacter;
1221   for( ; !matched && ( visualIndex < endCharacter ); ++visualIndex )
1222   {
1223     // The character in logical order.
1224     const CharacterIndex characterLogicalOrderIndex = hasRightToLeftCharacters ? *( visualToLogicalBuffer + visualIndex ) : visualIndex;
1225
1226     // The first glyph for that character in logical order.
1227     const GlyphIndex glyphLogicalOrderIndex = *( charactersToGlyphBuffer + characterLogicalOrderIndex );
1228
1229     // The number of glyphs for that character
1230     const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterLogicalOrderIndex );
1231
1232     // Get the metrics for the group of glyphs.
1233     GlyphMetrics glyphMetrics;
1234     GetGlyphsMetrics( glyphLogicalOrderIndex,
1235                       numberOfGlyphs,
1236                       glyphMetrics,
1237                       mVisualModel,
1238                       mFontClient );
1239
1240     const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
1241
1242     // Find the mid-point of the area containing the glyph
1243     const float glyphCenter = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
1244
1245     if( visualX < glyphCenter )
1246     {
1247       matched = true;
1248       break;
1249     }
1250   }
1251
1252   // Return the logical position of the cursor in characters.
1253
1254   if( !matched )
1255   {
1256     visualIndex = endCharacter;
1257   }
1258
1259   logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
1260   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
1261   return logicalIndex;
1262 }
1263
1264 void Controller::Impl::GetCursorPosition( CharacterIndex logical,
1265                                           CursorInfo& cursorInfo )
1266 {
1267   // TODO: Check for multiline with \n, etc...
1268
1269   // Check if the logical position is the first or the last one of the text.
1270   const bool isFirstPosition = 0u == logical;
1271   const bool isLastPosition = mLogicalModel->mText.Count() == logical;
1272
1273   if( isFirstPosition && isLastPosition )
1274   {
1275     // There is zero characters. Get the default font.
1276
1277     FontId defaultFontId = 0u;
1278     if( NULL == mFontDefaults )
1279     {
1280       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
1281                                              EMPTY_STRING );
1282     }
1283     else
1284     {
1285       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1286     }
1287
1288     Text::FontMetrics fontMetrics;
1289     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
1290
1291     cursorInfo.lineHeight = fontMetrics.ascender - fontMetrics.descender;
1292     cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1293
1294     cursorInfo.primaryPosition.x = 1.f;
1295     cursorInfo.primaryPosition.y = 0.f;
1296
1297     // Nothing else to do.
1298     return;
1299   }
1300
1301   // Get the previous logical index.
1302   const CharacterIndex previousLogical = isFirstPosition ? 0u : logical - 1u;
1303
1304   // Decrease the logical index if it's the last one.
1305   if( isLastPosition )
1306   {
1307     --logical;
1308   }
1309
1310   // Get the direction of the character and the previous one.
1311   const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL;
1312
1313   CharacterDirection isCurrentRightToLeft = false;
1314   CharacterDirection isPreviousRightToLeft = false;
1315   if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right.
1316   {
1317     isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + logical );
1318     isPreviousRightToLeft = *( modelCharacterDirectionsBuffer + previousLogical );
1319   }
1320
1321   // Get the line where the character is laid-out.
1322   const LineRun* modelLines = mVisualModel->mLines.Begin();
1323
1324   const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( logical );
1325   const LineRun& line = *( modelLines + lineIndex );
1326
1327   // Get the paragraph's direction.
1328   const CharacterDirection isRightToLeftParagraph = line.direction;
1329
1330   // Check whether there is an alternative position:
1331
1332   cursorInfo.isSecondaryCursor = ( isCurrentRightToLeft != isPreviousRightToLeft ) ||
1333     ( isLastPosition && ( isRightToLeftParagraph != isCurrentRightToLeft ) );
1334
1335   // Set the line height.
1336   cursorInfo.lineHeight = line.ascender + -line.descender;
1337
1338   // Convert the cursor position into the glyph position.
1339   CharacterIndex characterIndex = logical;
1340   if( cursorInfo.isSecondaryCursor &&
1341       ( isRightToLeftParagraph != isCurrentRightToLeft ) )
1342   {
1343     characterIndex = previousLogical;
1344   }
1345
1346   const GlyphIndex currentGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
1347   const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
1348   const Length numberOfCharacters = *( mVisualModel->mCharactersPerGlyph.Begin() +currentGlyphIndex );
1349
1350   // Get the metrics for the group of glyphs.
1351   GlyphMetrics glyphMetrics;
1352   GetGlyphsMetrics( currentGlyphIndex,
1353                     numberOfGlyphs,
1354                     glyphMetrics,
1355                     mVisualModel,
1356                     mFontClient );
1357
1358   float interGlyphAdvance = 0.f;
1359   if( !isLastPosition &&
1360       ( numberOfCharacters > 1u ) )
1361   {
1362     const CharacterIndex firstIndex = *( mVisualModel->mGlyphsToCharacters.Begin() + currentGlyphIndex );
1363     interGlyphAdvance = static_cast<float>( characterIndex - firstIndex ) * glyphMetrics.advance / static_cast<float>( numberOfCharacters );
1364   }
1365
1366   // Get the glyph position and x bearing.
1367   const Vector2& currentPosition = *( mVisualModel->mGlyphPositions.Begin() + currentGlyphIndex );
1368
1369   // Set the cursor's height.
1370   cursorInfo.primaryCursorHeight = glyphMetrics.fontHeight;
1371
1372   // Set the position.
1373   cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + currentPosition.x + ( isCurrentRightToLeft ? glyphMetrics.advance : interGlyphAdvance );
1374   cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1375
1376   if( isLastPosition )
1377   {
1378     // The position of the cursor after the last character needs special
1379     // care depending on its direction and the direction of the paragraph.
1380
1381     if( cursorInfo.isSecondaryCursor )
1382     {
1383       // Need to find the first character after the last character with the paragraph's direction.
1384       // i.e l0 l1 l2 r0 r1 should find r0.
1385
1386       // TODO: check for more than one line!
1387       characterIndex = isRightToLeftParagraph ? line.characterRun.characterIndex : line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1u;
1388       characterIndex = mLogicalModel->GetLogicalCharacterIndex( characterIndex );
1389
1390       const GlyphIndex glyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + characterIndex );
1391       const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + characterIndex );
1392
1393       const Vector2& position = *( mVisualModel->mGlyphPositions.Begin() + glyphIndex );
1394
1395       // Get the metrics for the group of glyphs.
1396       GlyphMetrics glyphMetrics;
1397       GetGlyphsMetrics( glyphIndex,
1398                         numberOfGlyphs,
1399                         glyphMetrics,
1400                         mVisualModel,
1401                         mFontClient );
1402
1403       cursorInfo.primaryPosition.x = -glyphMetrics.xBearing + position.x + ( isRightToLeftParagraph ? 0.f : glyphMetrics.advance );
1404
1405       cursorInfo.primaryPosition.y = line.ascender - glyphMetrics.ascender;
1406     }
1407     else
1408     {
1409       if( !isCurrentRightToLeft )
1410       {
1411         cursorInfo.primaryPosition.x += glyphMetrics.advance;
1412       }
1413       else
1414       {
1415         cursorInfo.primaryPosition.x -= glyphMetrics.advance;
1416       }
1417     }
1418   }
1419
1420   // Set the alternative cursor position.
1421   if( cursorInfo.isSecondaryCursor )
1422   {
1423     // Convert the cursor position into the glyph position.
1424     const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
1425     const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
1426     const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
1427
1428     // Get the glyph position.
1429     const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
1430
1431     // Get the metrics for the group of glyphs.
1432     GlyphMetrics glyphMetrics;
1433     GetGlyphsMetrics( previousGlyphIndex,
1434                       numberOfGlyphs,
1435                       glyphMetrics,
1436                       mVisualModel,
1437                       mFontClient );
1438
1439     // Set the cursor position and height.
1440     cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
1441                                                                                        ( !isLastPosition && isCurrentRightToLeft )    ) ? glyphMetrics.advance : 0.f );
1442
1443     cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
1444
1445     cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
1446
1447     // Update the primary cursor height as well.
1448     cursorInfo.primaryCursorHeight *= 0.5f;
1449   }
1450 }
1451
1452 CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const
1453 {
1454   if( NULL == mEventData )
1455   {
1456     // Nothing to do if there is no text input.
1457     return 0u;
1458   }
1459
1460   CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1461
1462   const Script script = mLogicalModel->GetScript( index );
1463   const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
1464   const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
1465
1466   Length numberOfCharacters = 0u;
1467   if( TextAbstraction::LATIN == script )
1468   {
1469     // Prevents to jump the whole Latin ligatures like fi, ff, ...
1470     numberOfCharacters = 1u;
1471   }
1472   else
1473   {
1474     GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
1475     numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1476
1477     while( 0u == numberOfCharacters )
1478     {
1479       numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
1480       ++glyphIndex;
1481     }
1482   }
1483
1484   if( index < mEventData->mPrimaryCursorPosition )
1485   {
1486     cursorIndex -= numberOfCharacters;
1487   }
1488   else
1489   {
1490     cursorIndex += numberOfCharacters;
1491   }
1492
1493   return cursorIndex;
1494 }
1495
1496 void Controller::Impl::UpdateCursorPosition()
1497 {
1498   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this );
1499   if( NULL == mEventData )
1500   {
1501     // Nothing to do if there is no text input.
1502     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" );
1503     return;
1504   }
1505
1506   if( IsShowingPlaceholderText() )
1507   {
1508     // Do not want to use the place-holder text to set the cursor position.
1509
1510     // Use the line's height of the font's family set to set the cursor's size.
1511     // If there is no font's family set, use the default font.
1512     // Use the current alignment to place the cursor at the beginning, center or end of the box.
1513
1514     float lineHeight = 0.f;
1515
1516     FontId defaultFontId = 0u;
1517     if( NULL == mFontDefaults )
1518     {
1519       defaultFontId = mFontClient.GetFontId( EMPTY_STRING,
1520                                              EMPTY_STRING );
1521     }
1522     else
1523     {
1524       defaultFontId = mFontDefaults->GetFontId( mFontClient );
1525     }
1526
1527     Text::FontMetrics fontMetrics;
1528     mFontClient.GetFontMetrics( defaultFontId, fontMetrics );
1529
1530     lineHeight = fontMetrics.ascender - fontMetrics.descender;
1531
1532
1533     Vector2 cursorPosition;
1534
1535     switch( mLayoutEngine.GetHorizontalAlignment() )
1536     {
1537       case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1538       {
1539         cursorPosition.x = 1.f;
1540         break;
1541       }
1542       case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1543       {
1544         cursorPosition.x = floor( 0.5f * mControlSize.width );
1545         break;
1546       }
1547       case LayoutEngine::HORIZONTAL_ALIGN_END:
1548       {
1549         cursorPosition.x = mControlSize.width;
1550         break;
1551       }
1552     }
1553
1554     switch( mLayoutEngine.GetVerticalAlignment() )
1555     {
1556       case LayoutEngine::VERTICAL_ALIGN_TOP:
1557       {
1558         cursorPosition.y = 0.f;
1559         break;
1560       }
1561       case LayoutEngine::VERTICAL_ALIGN_CENTER:
1562       {
1563         cursorPosition.y = floorf( 0.5f * ( mControlSize.height - lineHeight ) );
1564         break;
1565       }
1566       case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1567       {
1568         cursorPosition.y = mControlSize.height - lineHeight;
1569         break;
1570       }
1571     }
1572
1573     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1574                                          cursorPosition.x,
1575                                          cursorPosition.y,
1576                                          lineHeight,
1577                                          lineHeight );
1578   }
1579   else
1580   {
1581     CursorInfo cursorInfo;
1582     GetCursorPosition( mEventData->mPrimaryCursorPosition,
1583                        cursorInfo );
1584
1585     const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1586     const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1587
1588     // Sets the cursor position.
1589     mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1590                                          cursorPosition.x,
1591                                          cursorPosition.y,
1592                                          cursorInfo.primaryCursorHeight,
1593                                          cursorInfo.lineHeight );
1594     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y );
1595
1596     // Sets the grab handle position.
1597     mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1598                                          cursorPosition.x,
1599                                          cursorPosition.y,
1600                                          cursorInfo.lineHeight );
1601
1602     if( cursorInfo.isSecondaryCursor )
1603     {
1604       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1605       mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1606                                            cursorInfo.secondaryPosition.x + offset.x,
1607                                            cursorInfo.secondaryPosition.y + offset.y,
1608                                            cursorInfo.secondaryCursorHeight,
1609                                            cursorInfo.lineHeight );
1610       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1611     }
1612     else
1613     {
1614       mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1615     }
1616   }
1617   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" );
1618 }
1619
1620 void Controller::Impl::UpdateSelectionHandle( HandleType handleType )
1621 {
1622   if( ( LEFT_SELECTION_HANDLE != handleType ) &&
1623       ( RIGHT_SELECTION_HANDLE != handleType ) )
1624   {
1625     return;
1626   }
1627
1628   const bool leftSelectionHandle = LEFT_SELECTION_HANDLE == handleType;
1629   const CharacterIndex index = leftSelectionHandle ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1630
1631   CursorInfo cursorInfo;
1632   GetCursorPosition( index,
1633                      cursorInfo );
1634
1635   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1636   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1637
1638   // Sets the grab handle position.
1639   mEventData->mDecorator->SetPosition( handleType,
1640                                        cursorPosition.x,
1641                                        cursorPosition.y,
1642                                        cursorInfo.lineHeight );
1643 }
1644
1645 void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
1646 {
1647   // Clamp between -space & 0 (and the text alignment).
1648   if( actualSize.width > mControlSize.width )
1649   {
1650     const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
1651     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
1652     mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
1653
1654     mEventData->mDecoratorUpdated = true;
1655   }
1656   else
1657   {
1658     mEventData->mScrollPosition.x = 0.f;
1659   }
1660 }
1661
1662 void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
1663 {
1664   // Clamp between -space & 0 (and the text alignment).
1665   if( actualSize.height > mControlSize.height )
1666   {
1667     const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
1668     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
1669     mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
1670
1671     mEventData->mDecoratorUpdated = true;
1672   }
1673   else
1674   {
1675     mEventData->mScrollPosition.y = 0.f;
1676   }
1677 }
1678
1679 void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
1680 {
1681   Vector2 offset;
1682   bool updateDecorator = false;
1683   if( position.x < 0.f )
1684   {
1685     offset.x = -position.x;
1686     mEventData->mScrollPosition.x += offset.x;
1687     updateDecorator = true;
1688   }
1689   else if( position.x > mControlSize.width )
1690   {
1691     offset.x = mControlSize.width - position.x;
1692     mEventData->mScrollPosition.x += offset.x;
1693     updateDecorator = true;
1694   }
1695
1696   if( updateDecorator && mEventData->mDecorator )
1697   {
1698     mEventData->mDecorator->UpdatePositions( offset );
1699   }
1700
1701   // TODO : calculate the vertical scroll.
1702 }
1703
1704 void Controller::Impl::ScrollTextToMatchCursor()
1705 {
1706   // Get the current cursor position in decorator coords.
1707   const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
1708
1709   // Calculate the new cursor position.
1710   CursorInfo cursorInfo;
1711   GetCursorPosition( mEventData->mPrimaryCursorPosition,
1712                      cursorInfo );
1713
1714   // Calculate the offset to match the cursor position before the character was deleted.
1715   mEventData->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x - mAlignmentOffset.x;
1716
1717   ClampHorizontalScroll( mVisualModel->GetActualSize() );
1718
1719   const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
1720   const Vector2 cursorPosition = cursorInfo.primaryPosition + offset;
1721
1722   // Sets the cursor position.
1723   mEventData->mDecorator->SetPosition( PRIMARY_CURSOR,
1724                                        cursorPosition.x,
1725                                        cursorPosition.y,
1726                                        cursorInfo.primaryCursorHeight,
1727                                        cursorInfo.lineHeight );
1728
1729   // Sets the grab handle position.
1730   mEventData->mDecorator->SetPosition( GRAB_HANDLE,
1731                                        cursorPosition.x,
1732                                        cursorPosition.y,
1733                                        cursorInfo.lineHeight );
1734
1735   if( cursorInfo.isSecondaryCursor )
1736   {
1737     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH );
1738     mEventData->mDecorator->SetPosition( SECONDARY_CURSOR,
1739                                          cursorInfo.secondaryPosition.x + offset.x,
1740                                          cursorInfo.secondaryPosition.y + offset.y,
1741                                          cursorInfo.secondaryCursorHeight,
1742                                          cursorInfo.lineHeight );
1743     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + offset.x, cursorInfo.secondaryPosition.y + offset.y );
1744   }
1745   else
1746   {
1747     mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
1748   }
1749 }
1750
1751 void Controller::Impl::RequestRelayout()
1752 {
1753   mControlInterface.RequestTextRelayout();
1754 }
1755
1756 } // namespace Text
1757
1758 } // namespace Toolkit
1759
1760 } // namespace Dali