Remove last character when pre-edit text is empty
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / bidirectional-support.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 // FILE HEADER
19 #include <dali-toolkit/internal/text/bidirectional-support.h>
20
21 // EXTERNAL INCLUDES
22 #include <memory.h>
23 #include <dali/devel-api/text-abstraction/bidirectional-support.h>
24
25 namespace Dali
26 {
27
28 namespace Toolkit
29 {
30
31 namespace Text
32 {
33
34 namespace
35 {
36
37 /**
38  * @brief Get the lines of a paragraph.
39  *
40  * @param[in] paragraphInfo The paragraph.
41  * @param[in] lines The lines.
42  * @param[in] lineIndex Index pointing the first line to be checked.
43  * @param[out] firstLine Index to the first line of the paragraph.
44  * @param[out] numberOfLines The number of lines.
45  */
46 void GetLines( const BidirectionalParagraphInfoRun& paragraphInfo,
47                const Vector<LineRun>& lines,
48                unsigned int lineIndex,
49                unsigned int& firstLine,
50                unsigned int& numberOfLines )
51 {
52   firstLine = lineIndex;
53   numberOfLines = 0u;
54
55   const CharacterIndex lastCharacterIndex = paragraphInfo.characterRun.characterIndex + paragraphInfo.characterRun.numberOfCharacters;
56   bool firstLineFound = false;
57
58   for( Vector<LineRun>::ConstIterator it = lines.Begin() + lineIndex,
59          endIt = lines.End();
60        it != endIt;
61        ++it )
62   {
63     const LineRun& line = *it;
64
65     if( ( line.characterRun.characterIndex + line.characterRun.numberOfCharacters > paragraphInfo.characterRun.characterIndex ) &&
66         ( lastCharacterIndex > line.characterRun.characterIndex ) )
67     {
68       firstLineFound = true;
69       ++numberOfLines;
70     }
71     else if( lastCharacterIndex <= line.characterRun.characterIndex )
72     {
73       // nothing else to do.
74       break;
75     }
76
77     if( !firstLineFound )
78     {
79       ++firstLine;
80     }
81   }
82 }
83
84 } // namespace
85
86 void SetBidirectionalInfo( const Vector<Character>& text,
87                            const Vector<ScriptRun>& scripts,
88                            const Vector<LineBreakInfo>& lineBreakInfo,
89                            Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo )
90 {
91   // Traverse the script runs. If there is one with a right to left script, create the bidirectional info for the paragraph containing that script is needed.
92   // From the bidirectional point of view, a paragraph is the piece of text between two LINE_MUST_BREAK.
93
94   // Index pointing the first character of the current paragraph.
95   CharacterIndex paragraphCharacterIndex = 0u;
96
97   // Pointer to the text buffer.
98   const Character* textBuffer = text.Begin();
99
100   // Pointer to the line break info buffer.
101   const LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
102
103   // The number of characters.
104   const Length numberOfCharacters = text.Count();
105
106   // Handle to the bidirectional info module in text-abstraction.
107   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
108
109   for( Vector<ScriptRun>::ConstIterator it = scripts.Begin(),
110          endIt = scripts.End();
111        it != endIt;
112        ++it )
113   {
114     const ScriptRun& scriptRun = *it;
115     const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters;
116
117     if( TextAbstraction::IsRightToLeftScript( scriptRun.script ) && // The script is right to left.
118         ( lastScriptRunIndex > paragraphCharacterIndex ) )          // It isn't part of a previous paragraph.
119     {
120       // Find the paragraphs which contains this script run.
121       // Consider:
122       //  1) Different paragraphs may contain this script run.
123       //  ------||------------------- rtl sr ------------------------||-------------------
124       //  --||----- p -----||------------------ p -------------||-------- p ------||------
125       //
126       //  2) The paragraph which contains this script run may contain other right to left script runs.
127       //  -----||--- rtl sr ---||---- ltr sr ----||---------- rtl sr -----------||--------
128       //  -----||---------------------------------- p -----------------------------------|
129
130       while( lastScriptRunIndex > paragraphCharacterIndex )
131       {
132         // There is a paragraph which contains the current script.
133
134         Length index = paragraphCharacterIndex;
135         while( ( index < numberOfCharacters ) && ( paragraphCharacterIndex < lastScriptRunIndex ) )
136         {
137           if( TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + index ) )
138           {
139             if( index >= scriptRun.characterRun.characterIndex )
140             {
141               // The Bidirectional run must have the same number of characters than the paragraph.
142               BidirectionalParagraphInfoRun bidirectionalRun;
143               bidirectionalRun.characterRun.characterIndex = paragraphCharacterIndex;
144               bidirectionalRun.characterRun.numberOfCharacters = ( index - paragraphCharacterIndex ) + 1u; // The must break character is part of the paragrah.
145
146               // Create the bidirectional info for the whole paragraph and store the index to the table with this info in the run.
147               bidirectionalRun.bidirectionalInfoIndex = bidirectionalSupport.CreateInfo( textBuffer + bidirectionalRun.characterRun.characterIndex,
148                                                                                          bidirectionalRun.characterRun.numberOfCharacters );
149
150               bidirectionalInfo.PushBack( bidirectionalRun );
151             }
152
153             // Update the character index of the next paragraph.
154             paragraphCharacterIndex = index + 1u;
155           }
156           ++index;
157         }
158
159         // The last character is always a must-break, so there is no need to check if there is characters left.
160       }
161     }
162   }
163 }
164
165 void ReorderLines( const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
166                    Vector<LineRun>& lineRuns,
167                    Vector<BidirectionalLineInfoRun>& lineInfoRuns )
168 {
169   // Handle to the bidirectional info module in text-abstraction.
170   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
171
172   // Keep an index to the first line to be checked if it's contained inside the paragraph.
173   // Avoids check the lines from the beginning for each paragraph.
174   unsigned int lineIndex = 0u;
175
176   for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
177          endIt = bidirectionalInfo.End();
178        it != endIt;
179        ++it )
180   {
181     const BidirectionalParagraphInfoRun& paragraphInfo = *it;
182     const CharacterDirection direction = bidirectionalSupport.GetParagraphDirection( paragraphInfo.bidirectionalInfoIndex );
183
184     // Get the lines for this paragraph.
185     unsigned int firstLine = 0u;
186     unsigned int numberOfLines = 0u;
187
188     // Get an index to the first line and the number of lines of the current paragraph.
189     GetLines( paragraphInfo,
190               lineRuns,
191               lineIndex,
192               firstLine,
193               numberOfLines );
194
195     lineIndex = firstLine + numberOfLines;
196
197     // Traverse the lines and reorder them
198     for( Vector<LineRun>::Iterator lineIt = lineRuns.Begin() + firstLine,
199            endLineIt = lineRuns.Begin() + firstLine + numberOfLines;
200            lineIt != endLineIt;
201          ++lineIt )
202     {
203       LineRun& line = *lineIt;
204
205       // Sets the paragraph's direction.
206       line.direction = direction;
207
208       // Creates a bidirectional info for the line run.
209       BidirectionalLineInfoRun lineInfoRun;
210       lineInfoRun.characterRun.characterIndex = line.characterRun.characterIndex;
211       lineInfoRun.characterRun.numberOfCharacters = line.characterRun.numberOfCharacters;
212       lineInfoRun.direction = direction;
213
214       // Allocate space for the conversion maps.
215       // The memory is freed after the visual to logical to visual conversion tables are built in the logical model.
216       lineInfoRun.visualToLogicalMap = reinterpret_cast<CharacterIndex*>( malloc( line.characterRun.numberOfCharacters * sizeof( CharacterIndex ) ) );
217
218       // Reorders the line.
219       bidirectionalSupport.Reorder( paragraphInfo.bidirectionalInfoIndex,
220                                     line.characterRun.characterIndex - paragraphInfo.characterRun.characterIndex,
221                                     line.characterRun.numberOfCharacters,
222                                     lineInfoRun.visualToLogicalMap );
223
224       // Push the run into the vector.
225       lineInfoRuns.PushBack( lineInfoRun );
226     }
227   }
228 }
229
230 bool GetMirroredText( const Vector<Character>& text,
231                       Vector<Character>& mirroredText )
232 {
233   // Handle to the bidirectional info module in text-abstraction.
234   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
235
236   mirroredText = text;
237
238   return bidirectionalSupport.GetMirroredText( mirroredText.Begin(),
239                                                mirroredText.Count() );
240 }
241
242 void GetCharactersDirection( const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
243                              Vector<CharacterDirection>& directions )
244 {
245   // Handle to the bidirectional info module in text-abstraction.
246   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
247
248   CharacterIndex index = 0u;
249   CharacterDirection* directionsBuffer = directions.Begin();
250   for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
251          endIt = bidirectionalInfo.End();
252        it != endIt;
253        ++it )
254   {
255     const BidirectionalParagraphInfoRun& paragraph = *it;
256
257     // Fills with left to right those paragraphs without right to left characters.
258     memset( directionsBuffer + index, false, ( paragraph.characterRun.characterIndex - index ) * sizeof( bool ) );
259     index += paragraph.characterRun.numberOfCharacters;
260
261     bidirectionalSupport.GetCharactersDirection( paragraph.bidirectionalInfoIndex,
262                                                  directionsBuffer + paragraph.characterRun.characterIndex,
263                                                  paragraph.characterRun.numberOfCharacters );
264   }
265
266   // Fills with left to right those paragraphs without right to left characters.
267   memset( directionsBuffer + index, false, ( directions.Count() - index ) * sizeof( bool ) );
268 }
269
270 } // namespace Text
271
272 } // namespace Toolkit
273
274 } // namespace Dali