1 /*
2     This file is part of the Okteta Gui library, made within the KDE community.
3 
4     SPDX-FileCopyrightText: 2003, 2008-2009 Friedrich W. H. Kossebau <kossebau@kde.org>
5 
6     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 */
8 
9 #include "bytearraytableranges.hpp"
10 #include "bytearraytableranges_p.hpp"
11 
12 // lib
13 #include "bytearraytablelayout.hpp"
14 // Okteta core
15 #include <Okteta/ArrayChangeMetricsList>
16 
17 namespace Okteta {
18 
ByteArrayTableRanges(ByteArrayTableLayout * layout)19 ByteArrayTableRanges::ByteArrayTableRanges(ByteArrayTableLayout* layout)
20     : d_ptr(new ByteArrayTableRangesPrivate(layout))
21 {
22 }
23 
24 ByteArrayTableRanges::~ByteArrayTableRanges() = default;
25 
reset()26 void ByteArrayTableRanges::reset()
27 {
28     Q_D(ByteArrayTableRanges);
29     mSelection.cancel();
30     FirstWordSelection.unset();
31     mMarking.unset();
32     ChangedRanges.clear();
33     d->previousSelection = mSelection;
34 }
35 
setMarking(const AddressRange & marking)36 void ByteArrayTableRanges::setMarking(const AddressRange& marking)
37 {
38     if (mMarking == marking) {
39         return;
40     }
41 
42     const bool hadMarking = mMarking.isValid();
43     if (hadMarking) {
44         addChangedRange(mMarking);
45     }
46 
47     mMarking = marking;
48 
49     const bool hasNewMarking = mMarking.isValid();
50     if (hasNewMarking) {
51         addChangedRange(mMarking);
52     }
53 }
54 
removeFurtherSelections()55 void ByteArrayTableRanges::removeFurtherSelections()
56 {
57     for (int i = 1; i < noOfSelections(); ++i) {
58         removeSelection(i);
59     }
60 }
61 
setSelection(const AddressRange & selection)62 void ByteArrayTableRanges::setSelection(const AddressRange& selection)
63 {
64     bool Changed = mSelection.isValid();
65     if (Changed) {
66         addChangedRange(mSelection.range());
67     }
68     mSelection = selection;
69     addChangedRange(mSelection.range());
70 }
71 
setSelectionStart(Address startIndex)72 void ByteArrayTableRanges::setSelectionStart(Address startIndex)
73 {
74     bool Changed = mSelection.isValid();
75     if (Changed) {
76         addChangedRange(mSelection.range());
77     }
78 
79     mSelection.setStart(startIndex);
80 }
81 
setSelectionEnd(Address EndIndex)82 void ByteArrayTableRanges::setSelectionEnd(Address EndIndex)
83 {
84     AddressRange OldSelection = mSelection.range();
85     mSelection.setEnd(EndIndex);
86 
87     // TODO: think about rather building a diff of the sections
88     if (!OldSelection.isValid()) {
89         addChangedRange(mSelection.range());
90         return;
91     }
92     if (!mSelection.isValid()) {
93         addChangedRange(OldSelection);
94         return;
95     }
96 
97     if (OldSelection == mSelection.range()) {
98         return;
99     }
100     Address CS;
101     Address CE;
102     // changes at the end?
103     if (mSelection.start() == OldSelection.start()) {
104         CS = OldSelection.nextBehindEnd();
105         CE = mSelection.end();
106         if (CE < CS) {
107             CS = mSelection.nextBehindEnd();
108             CE = OldSelection.end();
109         }
110     }
111     // changes at the start?
112     else if (mSelection.end() == OldSelection.end()) {
113         CS = OldSelection.start();
114         CE = mSelection.nextBeforeStart();
115         if (CE < CS) {
116             CS = mSelection.start();
117             CE = OldSelection.nextBeforeStart();
118         }
119     }
120     // change over the anchor
121     else {
122         CS = OldSelection.start();
123         CE = mSelection.end();
124         if (CE < CS) {
125             CS = mSelection.start();
126             CE = OldSelection.end();
127         }
128     }
129     AddressRange C(CS, CE);
130 
131     bool Changed = C.isValid();
132     if (Changed) {
133         addChangedRange(C);
134     }
135     return;
136 }
137 
removeSelection(int id)138 AddressRange ByteArrayTableRanges::removeSelection(int id)
139 {
140     if (id > 0) {
141         return AddressRange();
142     }
143 
144     AddressRange range = mSelection.range();
145     bool Changed = range.isValid();
146     if (Changed) {
147         addChangedRange(range);
148     }
149 
150     mSelection.cancel();
151     FirstWordSelection.unset();
152 
153     return range;
154 }
155 
overlapsSelection(Address FirstIndex,Address LastIndex,Address * startIndex,Address * endIndex) const156 bool ByteArrayTableRanges::overlapsSelection(Address FirstIndex, Address LastIndex, Address* startIndex, Address* endIndex) const
157 {
158     if (mSelection.range().overlaps(AddressRange(FirstIndex, LastIndex))) {
159         *startIndex = mSelection.start();
160         *endIndex = mSelection.end();
161         return true;
162     }
163     return false;
164 }
165 
overlapsMarking(Address FirstIndex,Address LastIndex,Address * startIndex,Address * endIndex) const166 bool ByteArrayTableRanges::overlapsMarking(Address FirstIndex, Address LastIndex, Address* startIndex, Address* endIndex) const
167 {
168     if (mMarking.overlaps(AddressRange(FirstIndex, LastIndex))) {
169         *startIndex = mMarking.start();
170         *endIndex = mMarking.end();
171         return true;
172     }
173     return false;
174 }
175 
firstOverlappingSelection(const AddressRange & Range) const176 const AddressRange* ByteArrayTableRanges::firstOverlappingSelection(const AddressRange& Range) const
177 {
178     return mSelection.range().overlaps(Range) ? &mSelection.range() : nullptr;
179 }
180 
overlappingMarking(const AddressRange & Range) const181 const AddressRange* ByteArrayTableRanges::overlappingMarking(const AddressRange& Range) const
182 {
183     return mMarking.overlaps(Range) ? &mMarking : nullptr;
184 }
185 
186 /*
187 bool ByteArrayTableRanges::overlapsChanges( Address FirstIndex, Address LastIndex, Address* startIndex, Address* endIndex ) const
188 {
189     for( CoordRangeList::const_iterator S=ChangedRanges.begin(); S!=ChangedRanges.end(); ++S )
190     {
191         if( (*S).overlaps(KBuff(FirstIndex,LastIndex)) )
192         {
193             *startIndex = (*S).start();
194             *endIndex = (*S).end();
195             return true;
196         }
197     }
198 
199     return false;
200 }
201 
202 bool ByteArrayTableRanges::overlapsChanges( AddressRange Indizes, AddressRange *ChangedRange ) const
203 {
204     for( AddressRangeList::const_iterator S=ChangedRanges.begin(); S!=ChangedRanges.end(); ++S )
205     {
206         if( (*S).overlaps(Indizes) )
207         {
208             *ChangedRange = *S;
209             return true;
210         }
211     }
212 
213     return false;
214 }
215 */
216 
overlapsChanges(const CoordRange & Range,CoordRange * ChangedRange) const217 bool ByteArrayTableRanges::overlapsChanges(const CoordRange& Range, CoordRange* ChangedRange) const
218 {
219     // TODO: add a lastusedrange pointer for quicker access
220     return std::any_of(ChangedRanges.begin(), ChangedRanges.end(),
221                        [Range, ChangedRange](const CoordRange& changedRange) mutable {
222         if (changedRange.overlaps(Range)) {
223             *ChangedRange = changedRange;
224             return true;
225         }
226         return false;
227     });
228 }
229 
addChangedOffsetLines(const LineRange & changedLines)230 void ByteArrayTableRanges::addChangedOffsetLines(const LineRange& changedLines)
231 {
232     if (mChangedOffsetLines.isEmpty()) {
233         mChangedOffsetLines = changedLines;
234         mModified = true;
235     } else {
236         mChangedOffsetLines.extendTo(changedLines);
237     }
238 }
239 
addChangedRange(Address startIndex,Address endIndex)240 void ByteArrayTableRanges::addChangedRange(Address startIndex, Address endIndex)
241 {
242     addChangedRange(AddressRange(startIndex, endIndex));
243 }
244 
addChangedRange(const AddressRange & range)245 void ByteArrayTableRanges::addChangedRange(const AddressRange& range)
246 {
247     Q_D(ByteArrayTableRanges);
248 // qCDebug(LOG_OKTETA_GUI) << "adding change range "<<S.start()<<","<<S.end();
249     addChangedRange(d->layout->coordRangeOfIndizes(range));
250 }
251 
addChangedRange(const CoordRange & range)252 void ByteArrayTableRanges::addChangedRange(const CoordRange& range)
253 {
254     ChangedRanges.addCoordRange(range);
255 // qCDebug(LOG_OKTETA_GUI) << "as range "<<NewRange.start().pos()<<","<<NewRange.start().line()<<"-"
256 // <<NewRange.end().pos()<<","<<NewRange.end().line()<<endl;
257 
258     mModified = true;
259 }
260 
resetChangedRanges()261 void ByteArrayTableRanges::resetChangedRanges()
262 {
263     mChangedOffsetLines.unset();
264     ChangedRanges.clear();
265     mModified = false;
266 }
267 
takeHasSelectionChanged(bool * hasSelectionChanged,bool * selectionChanged)268 void ByteArrayTableRanges::takeHasSelectionChanged(bool* hasSelectionChanged, bool* selectionChanged)
269 {
270     Q_D(ByteArrayTableRanges);
271 
272     const bool hadSelection = d->previousSelection.isValid();
273     const bool hasSelection = mSelection.isValid();
274     *hasSelectionChanged = (hadSelection != hasSelection);
275 
276     *selectionChanged = (d->previousSelection != mSelection);
277 
278     if (*selectionChanged) {
279         d->previousSelection = mSelection;
280     }
281 }
282 
setFirstWordSelection(const AddressRange & range)283 void ByteArrayTableRanges::setFirstWordSelection(const AddressRange& range)
284 {
285     FirstWordSelection = range;
286     setSelection(FirstWordSelection);
287 }
288 
ensureWordSelectionForward(bool Forward)289 void ByteArrayTableRanges::ensureWordSelectionForward(bool Forward)
290 {
291     // in the anchor not on the right side?
292     if (mSelection.isForward() != Forward) {
293         setSelectionEnd(Forward ? FirstWordSelection.start() : FirstWordSelection.nextBehindEnd());
294 
295         mSelection.setForward(Forward);
296     }
297 }
298 
adaptToChanges(const ArrayChangeMetricsList & changeList,Size oldLength)299 void ByteArrayTableRanges::adaptToChanges(const ArrayChangeMetricsList& changeList, Size oldLength)
300 {
301     for (const ArrayChangeMetrics& change : changeList) {
302         // TODO: change parameters to ArrayChangeMetrics
303         switch (change.type())
304         {
305         case ArrayChangeMetrics::Replacement:
306         {
307             oldLength += change.lengthChange();
308             const Address offset = change.offset();
309             const Size diff = change.lengthChange();
310             const Address behindLast = (diff == 0) ? offset + change.insertLength() :
311                                        (diff < 0) ?  oldLength - diff :
312                                                      oldLength;
313             addChangedRange(offset, behindLast - 1);
314 
315             if (mSelection.isValid()) {
316                 mSelection.adaptToReplacement(offset, change.removeLength(), change.insertLength());
317             }
318             if (mMarking.isValid()) {
319                 mMarking.adaptToReplacement(offset, change.removeLength(), change.insertLength());
320             }
321             break;
322         }
323         case ArrayChangeMetrics::Swapping:
324             addChangedRange(change.offset(), change.secondEnd());
325 
326             if (mSelection.isValid()) {
327                 mSelection.adaptToSwap(change.offset(), change.secondStart(), change.secondLength());
328             }
329         // TODO:
330 //             if( mMarking.isValid() )
331 //                 mMarking.adaptToSwap( change.offset(), change.secondStart(), change.secondLength() );
332         default:
333             ;
334         }
335     }
336 }
337 
338 }
339