File indexing completed on 2024-04-21 16:34:01

0001 /*
0002     This file is part of the Okteta Gui library, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2003, 2008-2009 Friedrich W. H. Kossebau <kossebau@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include "bytearraytablecursor.hpp"
0010 
0011 // lib
0012 #include "bytearraytablelayout.hpp"
0013 // Okteta core
0014 #include <Okteta/ArrayChangeMetricsList>
0015 
0016 namespace Okteta {
0017 
0018 ByteArrayTableCursor::ByteArrayTableCursor(const ByteArrayTableLayout* layout)
0019     : mLayout(layout)
0020     , mIndex(layout->byteArrayOffset())
0021     , mCoord(layout->startCoord())
0022     , mBehind(false)
0023     , mAppendPosEnabled(false)
0024 {
0025 }
0026 
0027 ByteArrayTableCursor::~ByteArrayTableCursor() = default;
0028 
0029 void ByteArrayTableCursor::setAppendPosEnabled(bool appendPosEnabled)
0030 {
0031     if (mAppendPosEnabled == appendPosEnabled) {
0032         return;
0033     }
0034 
0035     mAppendPosEnabled = appendPosEnabled;
0036     // reposition Cursor
0037     if (realIndex() > mLayout->lastByteArrayOffset()
0038         && mCoord.pos() < mLayout->noOfBytesPerLine() - 1
0039         && mLayout->length() > 0) {
0040         if (mAppendPosEnabled) {
0041             ++mIndex;
0042             mCoord.goRight();
0043             mBehind = false;
0044         } else {
0045             --mIndex;
0046             mCoord.goLeft();
0047             mBehind = true;
0048         }
0049     }
0050 }
0051 
0052 void ByteArrayTableCursor::gotoPreviousByte()
0053 {
0054     if (mBehind) {
0055         mBehind = false;
0056     } else if (mIndex > mLayout->byteArrayOffset()) {
0057         --mIndex;
0058         mCoord.goCLeft(mLayout->noOfBytesPerLine() - 1);
0059     }
0060 }
0061 
0062 void ByteArrayTableCursor::gotoPreviousByte(Size indexSteps)
0063 {
0064     if (mBehind) {
0065         --indexSteps;
0066         mBehind = false;
0067     }
0068     const Address newIndex = mIndex - indexSteps;
0069     // would step before first position?
0070     const Address firstIndex = mLayout->byteArrayOffset();
0071     if (newIndex < firstIndex) {
0072         if (mIndex > firstIndex) {
0073             gotoStart();
0074         }
0075     } else {
0076         gotoIndex(newIndex);
0077     }
0078 }
0079 
0080 void ByteArrayTableCursor::gotoNextByte()
0081 {
0082     const Address lastIndex = mLayout->lastByteArrayOffset();
0083 
0084     if (mIndex < lastIndex) {
0085         ++mIndex;
0086         mCoord.goCRight(mLayout->noOfBytesPerLine() - 1);
0087         mBehind = false;
0088     } else if (mIndex == lastIndex) {
0089         stepToEnd();
0090     }
0091 }
0092 
0093 void ByteArrayTableCursor::gotoNextByte(Size indexSteps)   // TODO: think about consistency with gotoNextByte!!!
0094 {
0095     if (mBehind) {
0096         ++indexSteps;
0097         mBehind = false;
0098     }
0099     const Address newIndex = mIndex + indexSteps;
0100     // would step behind the end?
0101     if (newIndex > mLayout->lastByteArrayOffset()) {
0102         gotoEnd();
0103     } else {
0104         gotoIndex(newIndex);
0105     }
0106 }
0107 
0108 void ByteArrayTableCursor::gotoUp()
0109 {
0110     // can we even go up?
0111     if (mCoord.isBelow(mLayout->startLine())) {
0112         mCoord.goUp();
0113         if (mCoord.isPriorInLineThan(mLayout->startCoord())) {
0114             mIndex = mLayout->byteArrayOffset();
0115             mCoord.setPos(mLayout->firstStartLinePosition());
0116             mBehind = false;
0117         } else {
0118             mIndex -= mLayout->noOfBytesPerLine();
0119             if (mBehind && !atLineEnd()) {
0120                 ++mIndex;
0121                 mCoord.goRight();
0122                 mBehind = false;
0123             }
0124         }
0125     }
0126 }
0127 
0128 void ByteArrayTableCursor::gotoDown()
0129 {
0130     if (mCoord.isAbove(mLayout->finalLine())) {
0131         mCoord.goDown();
0132         // behind End?
0133         if (mCoord.isLaterInLineThan(mLayout->finalCoord())) {
0134             gotoEnd();
0135         } else {
0136             mIndex += mLayout->noOfBytesPerLine();
0137         }
0138     }
0139 }
0140 
0141 void ByteArrayTableCursor::gotoLineStart()
0142 {
0143     const Address oldIndex = mIndex;
0144     mIndex = mLayout->indexAtFirstLinePosition(mCoord.line());
0145     mCoord.goLeft(oldIndex - mIndex);
0146     mBehind = false;
0147 }
0148 
0149 void ByteArrayTableCursor::gotoLineEnd()
0150 {
0151     if (mIndex <= mLayout->lastByteArrayOffset()) {
0152         const Address oldIndex = mIndex;
0153         mIndex = mLayout->indexAtLastLinePosition(mCoord.line());
0154         mCoord.goRight(mIndex - oldIndex);
0155 
0156         stepToEnd();
0157     }
0158 }
0159 
0160 void ByteArrayTableCursor::gotoStart()
0161 {
0162     mIndex = mLayout->byteArrayOffset();
0163     mCoord = mLayout->startCoord();
0164     mBehind = false;
0165 }
0166 
0167 void ByteArrayTableCursor::gotoEnd()
0168 {
0169     const Address lastIndex = mLayout->lastByteArrayOffset();
0170     if (lastIndex >= 0) {
0171         mIndex = lastIndex;
0172         mCoord = mLayout->finalCoord();
0173 
0174         stepToEnd();
0175     } else {
0176         gotoStart();
0177     }
0178 }
0179 
0180 void ByteArrayTableCursor::gotoCIndex(Address index)
0181 {
0182     if (mLayout->length() > 0) {
0183         mIndex = mLayout->correctIndex(index);
0184         mCoord = mLayout->coordOfIndex(mIndex);
0185         mBehind = (index > mIndex);
0186     } else {
0187         gotoStart();
0188     }
0189 }
0190 
0191 void ByteArrayTableCursor::gotoCCoord(Coord coord)
0192 {
0193     if (mLayout->length() > 0) {
0194         mCoord = mLayout->correctCoord(coord);
0195         mIndex = mLayout->indexAtCoord(mCoord);
0196         if (coord > mCoord) {
0197             stepToEnd();
0198         } else {
0199             mBehind = false;
0200         }
0201     } else {
0202         gotoStart();
0203     }
0204 }
0205 
0206 void ByteArrayTableCursor::stepToEnd()
0207 {
0208     if (mAppendPosEnabled && (mCoord.pos() < mLayout->noOfBytesPerLine() - 1)) {
0209         ++mIndex;
0210         mCoord.goRight();
0211         mBehind = false;
0212     } else {
0213         mBehind = true;
0214     }
0215 }
0216 
0217 void ByteArrayTableCursor::gotoIndex(Address index)
0218 {
0219     mIndex = index;
0220     mCoord = mLayout->coordOfIndex(mIndex);
0221     mBehind = false;
0222 }
0223 
0224 void ByteArrayTableCursor::gotoRealIndex()
0225 {
0226     if (mBehind
0227         && (mAppendPosEnabled || (mIndex < mLayout->lastByteArrayOffset()))) {
0228         ++mIndex;
0229         mCoord.goCRight(mLayout->noOfBytesPerLine() - 1);
0230         mBehind = false;
0231     }
0232 }
0233 
0234 void ByteArrayTableCursor::gotoCoord(Coord coord)
0235 {
0236     mIndex = mLayout->indexAtCoord(coord);
0237     mCoord = coord;
0238     mBehind = false;
0239 }
0240 
0241 void ByteArrayTableCursor::updateCoord()
0242 {
0243     mCoord = mLayout->coordOfIndex(mIndex);
0244 }
0245 
0246 // page down should be: one page minus one line
0247 // -> if in the very first line page down will put the cursor on the same page into the last line
0248 void ByteArrayTableCursor::gotoPageUp()
0249 {
0250     const LineSize noOfLinesPerPage = mLayout->noOfLinesPerPage();
0251     const Address newIndex = mIndex - noOfLinesPerPage * mLayout->noOfBytesPerLine();
0252     if (newIndex >= mLayout->byteArrayOffset()) {
0253         mIndex = newIndex;
0254         mCoord.goUp(noOfLinesPerPage);
0255         if (mBehind && !atLineEnd()) {
0256             ++mIndex;
0257             mCoord.goRight();
0258             mBehind = false;
0259         }
0260     } else {
0261         gotoStart();
0262     }
0263 }
0264 
0265 void ByteArrayTableCursor::gotoPageDown()
0266 {
0267     const LineSize noOfLinesPerPage = mLayout->noOfLinesPerPage();
0268     const Address newIndex = mIndex + noOfLinesPerPage * mLayout->noOfBytesPerLine();
0269     if (newIndex <= mLayout->lastByteArrayOffset()) {
0270         mIndex = newIndex;
0271         mCoord.goDown(noOfLinesPerPage);
0272     } else {
0273         gotoEnd();
0274     }
0275 }
0276 
0277 Address ByteArrayTableCursor::validIndex() const
0278 {
0279     return (mLayout->byteArrayOffset() <= mIndex && mIndex <= mLayout->lastByteArrayOffset()) ? mIndex : -1;
0280 }
0281 
0282 Address ByteArrayTableCursor::indexAtLineStart() const { return mLayout->indexAtFirstLinePosition(mCoord.line()); }
0283 Address ByteArrayTableCursor::indexAtLineEnd()   const { return mLayout->indexAtLastLinePosition(mCoord.line()); }
0284 
0285 bool ByteArrayTableCursor::atStart()     const { return mIndex == mLayout->byteArrayOffset(); }
0286 bool ByteArrayTableCursor::atEnd()       const { return realIndex() == mLayout->lastByteArrayOffset() + 1; }
0287 bool ByteArrayTableCursor::atAppendPos() const { return mIndex == mLayout->lastByteArrayOffset() + 1; }
0288 
0289 bool ByteArrayTableCursor::atLineStart() const { return mLayout->atFirstLinePosition(mCoord); }
0290 bool ByteArrayTableCursor::atLineEnd()   const { return mLayout->atLastLinePosition(mCoord); }
0291 
0292 // TODO: oldLength is a hack, as DataLayout is already updated and used by e.g. gotoCIndex
0293 void ByteArrayTableCursor::adaptToChanges(const ArrayChangeMetricsList& changeList, Size oldLength)
0294 {
0295     for (const ArrayChangeMetrics& change : changeList) {
0296         // cursor affected?
0297         if (mIndex >= change.offset()) {
0298             switch (change.type())
0299             {
0300             case ArrayChangeMetrics::Replacement:
0301                 oldLength += change.lengthChange();
0302                 if (oldLength > 0) {
0303                     const Address newIndex =
0304                         // cursor behind the removed section?
0305                         (mIndex >= change.offset() + change.removeLength()) ? mIndex + change.lengthChange() :
0306                         // cursor at substituted section?
0307                         (mIndex < change.offset() + change.insertLength()) ?  mIndex :
0308                         // cursor at unsubstituted section
0309                                                                               change.offset() + change.insertLength();
0310 
0311                     // if the cursor gets behind, it will never get inside again.
0312                     if (newIndex >= oldLength) {
0313                         gotoEnd();
0314                         return;
0315                     }
0316                     mIndex = newIndex;
0317                 }
0318                 // if the cursor gets at the start, it will stay there
0319                 else {
0320                     gotoStart();
0321                     return;
0322                 }
0323                 break;
0324             case ArrayChangeMetrics::Swapping:
0325                 if (mIndex < change.secondStart()) {
0326                     mIndex += change.secondLength();
0327                 } else if (mIndex <= change.secondEnd()) {
0328                     mIndex -= change.firstLength();
0329                 }
0330                 break;
0331             default:
0332                 ;
0333             }
0334         }
0335     }
0336 
0337     const bool wasBehind = (mIndex >= oldLength);
0338     if (wasBehind) {
0339         mIndex = oldLength - 1;
0340     }
0341     updateCoord();
0342     if (wasBehind) {
0343         stepToEnd();
0344     }
0345 }
0346 
0347 }