Remove useless iteration when debug mode
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / bounded-paragraph-helper-functions.cpp
1 /*
2  * Copyright (c) 2022 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/bounded-paragraph-helper-functions.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/devel-api/text-abstraction/script.h>
23
24 namespace Dali
25 {
26 namespace Toolkit
27 {
28 namespace Text
29 {
30 void MergeBoundedParagraphRunsWhenRemoveCharacters(const Vector<Character>&     text,
31                                                    const CharacterIndex&        index,
32                                                    const int&                   numberOfCharacters,
33                                                    Vector<BoundedParagraphRun>& boundedParagraphRuns)
34 {
35   // This works on boundedParagraphRuns before applying on them the changes of the removed characters.
36   // Update to merge only when characters have been removed.
37   if(numberOfCharacters >= 0)
38   {
39     return;
40   }
41
42   //No runs to merge
43   if(boundedParagraphRuns.Count() == 0u)
44   {
45     return;
46   }
47
48   const Length          numberOfRemovedCharacters     = (Length)(-1 * numberOfCharacters);
49   const Length          totalNumberOfCharacters       = text.Count();
50   const CharacterIndex& firstIndexOfRemovedCharacters = index;
51   const CharacterIndex  lastIndexOfRemovedCharacters  = ((firstIndexOfRemovedCharacters + numberOfRemovedCharacters) > 0u) ? (firstIndexOfRemovedCharacters + numberOfRemovedCharacters - 1u) : firstIndexOfRemovedCharacters; // Note: Length is uint32. Extra validation to avoid  a potential defects.
52
53   Length numberOfRuns          = boundedParagraphRuns.Count();
54   Length firstRunIndexToUpdate = 0;
55   Length lastRunIndexToUpdate  = 0;
56   bool   noNeedToMerge         = false;
57
58   // Find the first boundedParagraphRuns that is possible to be updated.
59   while(firstRunIndexToUpdate < numberOfRuns)
60   {
61     const CharacterIndex startCharIndex = boundedParagraphRuns[firstRunIndexToUpdate].characterRun.characterIndex;
62     const CharacterIndex endCharIndex   = boundedParagraphRuns[firstRunIndexToUpdate].characterRun.GetEndCharacterIndex();
63
64     // In-case the paragraph separator of the plain text (before the current bounded paragraph) is removed.
65     // In-case the start index of the current bounded paragraph is between start and end indices of removed characters.
66     // In-case the end index of the current bounded paragraph is between start and end indices of removed characters.
67     if((startCharIndex == lastIndexOfRemovedCharacters + 1u) ||
68        ((firstIndexOfRemovedCharacters <= startCharIndex) && (startCharIndex <= lastIndexOfRemovedCharacters)) ||
69        ((firstIndexOfRemovedCharacters <= endCharIndex) && (endCharIndex <= lastIndexOfRemovedCharacters)))
70     {
71       break;
72     }
73     else if(lastIndexOfRemovedCharacters + 1u < startCharIndex)
74     { // The whole removed characters are exist before the remaining bounded paragraphs.
75       noNeedToMerge = true;
76       break;
77     }
78     firstRunIndexToUpdate++;
79   }
80
81   // There is no run was affected by the removed characters.
82   if(noNeedToMerge || (firstRunIndexToUpdate == numberOfRuns))
83   {
84     return;
85   }
86
87   // Find the last boundedParagraphRuns that is possible to be updated.
88   lastRunIndexToUpdate = firstRunIndexToUpdate;
89   while(lastRunIndexToUpdate < numberOfRuns - 1u)
90   {
91     const CharacterIndex startCharIndex = boundedParagraphRuns[lastRunIndexToUpdate].characterRun.characterIndex;
92     const CharacterIndex endCharIndex   = boundedParagraphRuns[lastRunIndexToUpdate].characterRun.GetEndCharacterIndex();
93
94     if((lastIndexOfRemovedCharacters < endCharIndex) ||
95        (lastIndexOfRemovedCharacters + 1u <= startCharIndex))
96     {
97       break;
98     }
99
100     lastRunIndexToUpdate++;
101   }
102
103   // Remove all boundedParagraphRun between firstRunIndexToUpdate and lastRunIndexToUpdate
104   // At least one boundedParagraphRun between firstRunIndexToUpdate and lastRunIndexToUpdate
105   if(firstRunIndexToUpdate + 1u < lastRunIndexToUpdate)
106   {
107     Length runIndexToDelete = firstRunIndexToUpdate + 1u;
108
109     while(runIndexToDelete < lastRunIndexToUpdate)
110     {
111       Dali::Vector<BoundedParagraphRun>::Iterator paragraphToDelete = boundedParagraphRuns.Begin() + (runIndexToDelete);
112       boundedParagraphRuns.Remove(paragraphToDelete);
113
114       lastRunIndexToUpdate--;
115       numberOfRuns--;
116     }
117   }
118
119   CharacterIndex endCharIndexFirstRun = boundedParagraphRuns[firstRunIndexToUpdate].characterRun.GetEndCharacterIndex();
120   ;
121   if(firstRunIndexToUpdate == lastRunIndexToUpdate)
122   {
123     if(endCharIndexFirstRun < lastIndexOfRemovedCharacters)
124     {
125       boundedParagraphRuns[firstRunIndexToUpdate].characterRun.numberOfCharacters += (lastIndexOfRemovedCharacters - endCharIndexFirstRun);
126     }
127   }
128   else
129   {
130     CharacterIndex startCharIndexLastRun = boundedParagraphRuns[lastRunIndexToUpdate].characterRun.characterIndex;
131     boundedParagraphRuns[firstRunIndexToUpdate].characterRun.numberOfCharacters += ((startCharIndexLastRun - endCharIndexFirstRun) > 0u) ? (startCharIndexLastRun - endCharIndexFirstRun - 1u) : 0u; // Note: Length is uint32. Extra validation to avoid  a potential defects.
132
133     CharacterIndex endCharIndexLastRun = boundedParagraphRuns[lastRunIndexToUpdate].characterRun.GetEndCharacterIndex();
134
135     if(endCharIndexLastRun < lastIndexOfRemovedCharacters)
136     {
137       boundedParagraphRuns[lastRunIndexToUpdate].characterRun.numberOfCharacters += (lastIndexOfRemovedCharacters - endCharIndexLastRun);
138     }
139   }
140
141   // Each endCharIndex for boundedParagraphRun is a paragraph separator.
142   // If not then keep adding characters until find paragraph separator.
143   Length runIndex = firstRunIndexToUpdate;
144   while(runIndex <= lastRunIndexToUpdate)
145   {
146     CharacterIndex endCharIndex = boundedParagraphRuns[runIndex].characterRun.GetEndCharacterIndex();
147
148     // The remaining text was not affected.
149     if(endCharIndex > lastIndexOfRemovedCharacters)
150     {
151       break;
152     }
153
154     // Reference for numberOfCharacters in current run to update it
155     CharacterIndex& numberOfCharactersInRun = boundedParagraphRuns[runIndex].characterRun.numberOfCharacters;
156
157     // In-case arrived to the start of the next run and there is no paragraph separator.
158     // Merging the next run with the current run. Removing the next run.
159     if((runIndex + 1u < numberOfRuns) &&
160        (endCharIndex <= lastIndexOfRemovedCharacters) &&
161        ((lastIndexOfRemovedCharacters + 1u) < totalNumberOfCharacters) &&
162        (!TextAbstraction::IsNewParagraph(*(text.Begin() + lastIndexOfRemovedCharacters + 1u))))
163     {
164       numberOfCharactersInRun += boundedParagraphRuns[runIndex + 1u].characterRun.numberOfCharacters;
165
166       Dali::Vector<BoundedParagraphRun>::Iterator paragraphToDelete = boundedParagraphRuns.Begin() + (runIndex + 1u);
167       boundedParagraphRuns.Remove(paragraphToDelete);
168
169       numberOfRuns--;
170       lastRunIndexToUpdate--;
171
172       continue;
173     }
174
175     runIndex++;
176   }
177
178   // Each startCharIndex-1u for boundedParagraphRun is a paragraph separator.
179   // If not then remove boundedParagraphRun.
180   if(numberOfRuns > 0u &&
181      firstIndexOfRemovedCharacters > 0u &&
182      boundedParagraphRuns[firstRunIndexToUpdate].characterRun.characterIndex > 0u)
183   {
184     const CharacterIndex startCharIndex = boundedParagraphRuns[firstRunIndexToUpdate].characterRun.characterIndex;
185
186     if(firstIndexOfRemovedCharacters <= startCharIndex && startCharIndex <= lastIndexOfRemovedCharacters + 1u)
187     {
188       // Verify the paragraph separator before the first run to update
189       // In-case the paragraph separator is removed before the boundedParagraphRun.
190       // Remove the boundedParagraphRun.
191       if((!TextAbstraction::IsNewParagraph(*(text.Begin() + firstIndexOfRemovedCharacters - 1u))) &&
192          (!TextAbstraction::IsNewParagraph(*(text.Begin() + startCharIndex))))
193       {
194         Dali::Vector<BoundedParagraphRun>::Iterator paragraphToDelete = boundedParagraphRuns.Begin() + (firstRunIndexToUpdate);
195         boundedParagraphRuns.Remove(paragraphToDelete);
196       }
197     }
198   }
199 }
200 } // namespace Text
201
202 } // namespace Toolkit
203
204 } // namespace Dali