File indexing completed on 2025-03-16 08:11:25
0001 /* 0002 This file is part of Konsole, an X terminal. 0003 SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 0007 This program is distributed in the hope that it will be useful, 0008 but WITHOUT ANY WARRANTY; without even the implied warranty of 0009 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0010 GNU General Public License for more details. 0011 0012 You should have received a copy of the GNU General Public License 0013 along with this program; if not, write to the Free Software 0014 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0015 02110-1301 USA. 0016 */ 0017 0018 // Own 0019 #include "History.h" 0020 0021 // System 0022 #include <algorithm> 0023 #include <cerrno> 0024 #include <cstdio> 0025 #include <cstdlib> 0026 #include <iostream> 0027 #include <sys/mman.h> 0028 #include <sys/types.h> 0029 #include <unistd.h> 0030 0031 #include <QtDebug> 0032 0033 // KDE 0034 // #include <kde_file.h> 0035 // #include <kdebug.h> 0036 0037 // Reasonable line size 0038 #define LINE_SIZE 1024 0039 #define KDE_lseek lseek 0040 0041 using namespace Konsole; 0042 0043 /* 0044 An arbitrary long scroll. 0045 0046 One can modify the scroll only by adding either cells 0047 or newlines, but access it randomly. 0048 0049 The model is that of an arbitrary wide typewriter scroll 0050 in that the scroll is a serie of lines and each line is 0051 a serie of cells with no overwriting permitted. 0052 0053 The implementation provides arbitrary length and numbers 0054 of cells and line/column indexed read access to the scroll 0055 at constant costs. 0056 0057 KDE4: Can we use QTemporaryFile here, instead of KTempFile? 0058 0059 FIXME: some complain about the history buffer comsuming the 0060 memory of their machines. This problem is critical 0061 since the history does not behave gracefully in cases 0062 where the memory is used up completely. 0063 0064 I put in a workaround that should handle it problem 0065 now gracefully. I'm not satisfied with the solution. 0066 0067 FIXME: Terminating the history is not properly indicated 0068 in the menu. We should throw a signal. 0069 0070 FIXME: There is noticeable decrease in speed, also. Perhaps, 0071 there whole feature needs to be revisited therefore. 0072 Disadvantage of a more elaborated, say block-oriented 0073 scheme with wrap around would be it's complexity. 0074 */ 0075 0076 // FIXME: tempory replacement for tmpfile 0077 // this is here one for debugging purpose. 0078 0079 // #define tmpfile xTmpFile 0080 0081 // History File /////////////////////////////////////////// 0082 0083 /* 0084 A Row(X) data type which allows adding elements to the end. 0085 */ 0086 0087 HistoryFile::HistoryFile() 0088 : ion(-1) 0089 , length(0) 0090 , fileMap(nullptr) 0091 , readWriteBalance(0) 0092 { 0093 if (tmpFile.open()) { 0094 tmpFile.setAutoRemove(true); 0095 ion = tmpFile.handle(); 0096 } 0097 } 0098 0099 HistoryFile::~HistoryFile() 0100 { 0101 if (fileMap) 0102 unmap(); 0103 } 0104 0105 // TODO: Mapping the entire file in will cause problems if the history file becomes exceedingly large, 0106 //(ie. larger than available memory). HistoryFile::map() should only map in sections of the file at a time, 0107 // to avoid this. 0108 void HistoryFile::map() 0109 { 0110 Q_ASSERT(fileMap == nullptr); 0111 0112 fileMap = (char *)mmap(nullptr, length, PROT_READ, MAP_PRIVATE, ion, 0); 0113 0114 // if mmap'ing fails, fall back to the read-lseek combination 0115 if (fileMap == MAP_FAILED) { 0116 readWriteBalance = 0; 0117 fileMap = nullptr; 0118 // qDebug() << __FILE__ << __LINE__ << ": mmap'ing history failed. errno = " << errno; 0119 } 0120 } 0121 0122 void HistoryFile::unmap() 0123 { 0124 int result = munmap(fileMap, length); 0125 Q_ASSERT(result == 0); 0126 Q_UNUSED(result); 0127 0128 fileMap = nullptr; 0129 } 0130 0131 bool HistoryFile::isMapped() const 0132 { 0133 return (fileMap != nullptr); 0134 } 0135 0136 void HistoryFile::add(const unsigned char *bytes, int len) 0137 { 0138 if (fileMap) 0139 unmap(); 0140 0141 readWriteBalance++; 0142 0143 int rc = 0; 0144 0145 rc = KDE_lseek(ion, length, SEEK_SET); 0146 if (rc < 0) { 0147 perror("HistoryFile::add.seek"); 0148 return; 0149 } 0150 rc = write(ion, bytes, len); 0151 if (rc < 0) { 0152 perror("HistoryFile::add.write"); 0153 return; 0154 } 0155 length += rc; 0156 } 0157 0158 void HistoryFile::get(unsigned char *bytes, int len, int loc) 0159 { 0160 // count number of get() calls vs. number of add() calls. 0161 // If there are many more get() calls compared with add() 0162 // calls (decided by using MAP_THRESHOLD) then mmap the log 0163 // file to improve performance. 0164 readWriteBalance--; 0165 if (!fileMap && readWriteBalance < MAP_THRESHOLD) 0166 map(); 0167 0168 if (fileMap) { 0169 for (int i = 0; i < len; i++) 0170 bytes[i] = fileMap[loc + i]; 0171 } else { 0172 int rc = 0; 0173 0174 if (loc < 0 || len < 0 || loc + len > length) 0175 fprintf(stderr, "getHist(...,%d,%d): invalid args.\n", len, loc); 0176 rc = KDE_lseek(ion, loc, SEEK_SET); 0177 if (rc < 0) { 0178 perror("HistoryFile::get.seek"); 0179 return; 0180 } 0181 rc = read(ion, bytes, len); 0182 if (rc < 0) { 0183 perror("HistoryFile::get.read"); 0184 return; 0185 } 0186 } 0187 } 0188 0189 int HistoryFile::len() 0190 { 0191 return length; 0192 } 0193 0194 // History Scroll abstract base class ////////////////////////////////////// 0195 0196 HistoryScroll::HistoryScroll(HistoryType *t) 0197 : m_histType(t) 0198 { 0199 } 0200 0201 HistoryScroll::~HistoryScroll() 0202 { 0203 delete m_histType; 0204 } 0205 0206 bool HistoryScroll::hasScroll() 0207 { 0208 return true; 0209 } 0210 0211 // History Scroll File ////////////////////////////////////// 0212 0213 /* 0214 The history scroll makes a Row(Row(Cell)) from 0215 two history buffers. The index buffer contains 0216 start of line positions which refere to the cells 0217 buffer. 0218 0219 Note that index[0] addresses the second line 0220 (line #1), while the first line (line #0) starts 0221 at 0 in cells. 0222 */ 0223 0224 HistoryScrollFile::HistoryScrollFile(const QString &logFileName) 0225 : HistoryScroll(new HistoryTypeFile(logFileName)) 0226 , m_logFileName(logFileName) 0227 { 0228 } 0229 0230 HistoryScrollFile::~HistoryScrollFile() 0231 { 0232 } 0233 0234 int HistoryScrollFile::getLines() 0235 { 0236 return index.len() / sizeof(int); 0237 } 0238 0239 int HistoryScrollFile::getLineLen(int lineno) 0240 { 0241 return (startOfLine(lineno + 1) - startOfLine(lineno)) / sizeof(Character); 0242 } 0243 0244 bool HistoryScrollFile::isWrappedLine(int lineno) 0245 { 0246 if (lineno >= 0 && lineno <= getLines()) { 0247 unsigned char flag; 0248 lineflags.get((unsigned char *)&flag, sizeof(unsigned char), (lineno) * sizeof(unsigned char)); 0249 return flag; 0250 } 0251 return false; 0252 } 0253 0254 int HistoryScrollFile::startOfLine(int lineno) 0255 { 0256 if (lineno <= 0) 0257 return 0; 0258 if (lineno <= getLines()) { 0259 if (!index.isMapped()) 0260 index.map(); 0261 0262 int res; 0263 index.get((unsigned char *)&res, sizeof(int), (lineno - 1) * sizeof(int)); 0264 return res; 0265 } 0266 return cells.len(); 0267 } 0268 0269 void HistoryScrollFile::getCells(int lineno, int colno, int count, std::span<Character> res) 0270 { 0271 cells.get((unsigned char *)res.data(), count * sizeof(Character), startOfLine(lineno) + colno * sizeof(Character)); 0272 } 0273 0274 void HistoryScrollFile::addCells(std::span<const Character> text, int count) 0275 { 0276 cells.add((unsigned char *)text.data(), count * sizeof(Character)); 0277 } 0278 0279 void HistoryScrollFile::addLine(bool previousWrapped) 0280 { 0281 if (index.isMapped()) 0282 index.unmap(); 0283 0284 int locn = cells.len(); 0285 index.add((unsigned char *)&locn, sizeof(int)); 0286 unsigned char flags = previousWrapped ? 0x01 : 0x00; 0287 lineflags.add((unsigned char *)&flags, sizeof(unsigned char)); 0288 } 0289 0290 // History Scroll Buffer ////////////////////////////////////// 0291 HistoryScrollBuffer::HistoryScrollBuffer(unsigned int maxLineCount) 0292 : HistoryScroll(new HistoryTypeBuffer(maxLineCount)) 0293 , _historyBuffer() 0294 , _maxLineCount(0) 0295 , _usedLines(0) 0296 , _head(0) 0297 { 0298 setMaxNbLines(maxLineCount); 0299 } 0300 0301 HistoryScrollBuffer::~HistoryScrollBuffer() 0302 { 0303 delete[] _historyBuffer; 0304 } 0305 0306 void HistoryScrollBuffer::addCellsVector(const QVector<Character> &cells) 0307 { 0308 _head++; 0309 if (_usedLines < _maxLineCount) 0310 _usedLines++; 0311 0312 if (_head >= _maxLineCount) { 0313 _head = 0; 0314 } 0315 0316 _historyBuffer[bufferIndex(_usedLines - 1)] = cells; 0317 _wrappedLine[bufferIndex(_usedLines - 1)] = false; 0318 } 0319 void HistoryScrollBuffer::addCells(std::span<const Character> a, int count) 0320 { 0321 HistoryLine newLine(count); 0322 std::copy(a.data(), a.subspan(count).data(), newLine.begin()); 0323 0324 addCellsVector(newLine); 0325 } 0326 0327 void HistoryScrollBuffer::addLine(bool previousWrapped) 0328 { 0329 _wrappedLine[bufferIndex(_usedLines - 1)] = previousWrapped; 0330 } 0331 0332 int HistoryScrollBuffer::getLines() 0333 { 0334 return _usedLines; 0335 } 0336 0337 int HistoryScrollBuffer::getLineLen(int lineNumber) 0338 { 0339 Q_ASSERT(lineNumber >= 0 && lineNumber < _maxLineCount); 0340 0341 if (lineNumber < _usedLines) { 0342 return _historyBuffer[bufferIndex(lineNumber)].size(); 0343 } else { 0344 return 0; 0345 } 0346 } 0347 0348 bool HistoryScrollBuffer::isWrappedLine(int lineNumber) 0349 { 0350 Q_ASSERT(lineNumber >= 0 && lineNumber < _maxLineCount); 0351 0352 if (lineNumber < _usedLines) { 0353 // kDebug() << "Line" << lineNumber << "wrapped is" << _wrappedLine[bufferIndex(lineNumber)]; 0354 return _wrappedLine[bufferIndex(lineNumber)]; 0355 } else 0356 return false; 0357 } 0358 0359 void HistoryScrollBuffer::getCells(int lineNumber, int startColumn, int count, std::span<Character> buffer) 0360 { 0361 if (count == 0) 0362 return; 0363 0364 Q_ASSERT(lineNumber < _maxLineCount); 0365 0366 if (lineNumber >= _usedLines) { 0367 memset(static_cast<void *>(buffer.data()), 0, count * sizeof(Character)); 0368 return; 0369 } 0370 0371 const HistoryLine &line = _historyBuffer[bufferIndex(lineNumber)]; 0372 0373 // kDebug() << "startCol " << startColumn; 0374 // kDebug() << "line.size() " << line.size(); 0375 // kDebug() << "count " << count; 0376 0377 Q_ASSERT(startColumn <= line.size() - count); 0378 0379 memcpy(buffer.data(), line.constData() + startColumn, count * sizeof(Character)); 0380 } 0381 0382 void HistoryScrollBuffer::setMaxNbLines(unsigned int lineCount) 0383 { 0384 HistoryLine *oldBuffer = _historyBuffer; 0385 HistoryLine *newBuffer = new HistoryLine[lineCount]; 0386 0387 for (int i = 0; i < qMin(_usedLines, (int)lineCount); i++) { 0388 newBuffer[i] = oldBuffer[bufferIndex(i)]; 0389 } 0390 0391 _usedLines = qMin(_usedLines, (int)lineCount); 0392 _maxLineCount = lineCount; 0393 _head = (_usedLines == _maxLineCount) ? 0 : _usedLines - 1; 0394 0395 _historyBuffer = newBuffer; 0396 delete[] oldBuffer; 0397 0398 _wrappedLine.resize(lineCount); 0399 dynamic_cast<HistoryTypeBuffer *>(m_histType)->m_nbLines = lineCount; 0400 } 0401 0402 int HistoryScrollBuffer::bufferIndex(int lineNumber) const 0403 { 0404 Q_ASSERT(lineNumber >= 0); 0405 Q_ASSERT(lineNumber < _maxLineCount); 0406 Q_ASSERT((_usedLines == _maxLineCount) || lineNumber <= _head); 0407 0408 if (_usedLines == _maxLineCount) { 0409 return (_head + lineNumber + 1) % _maxLineCount; 0410 } else { 0411 return lineNumber; 0412 } 0413 } 0414 0415 // History Scroll None ////////////////////////////////////// 0416 0417 HistoryScrollNone::HistoryScrollNone() 0418 : HistoryScroll(new HistoryTypeNone()) 0419 { 0420 } 0421 0422 HistoryScrollNone::~HistoryScrollNone() 0423 { 0424 } 0425 0426 bool HistoryScrollNone::hasScroll() 0427 { 0428 return false; 0429 } 0430 0431 int HistoryScrollNone::getLines() 0432 { 0433 return 0; 0434 } 0435 0436 int HistoryScrollNone::getLineLen(int) 0437 { 0438 return 0; 0439 } 0440 0441 bool HistoryScrollNone::isWrappedLine(int /*lineno*/) 0442 { 0443 return false; 0444 } 0445 0446 void HistoryScrollNone::getCells(int, int, int, std::span<Character>) 0447 { 0448 } 0449 0450 void HistoryScrollNone::addCells(std::span<const Character>, int) 0451 { 0452 } 0453 0454 void HistoryScrollNone::addLine(bool) 0455 { 0456 } 0457 0458 // History Scroll BlockArray ////////////////////////////////////// 0459 0460 HistoryScrollBlockArray::HistoryScrollBlockArray(size_t size) 0461 : HistoryScroll(new HistoryTypeBlockArray(size)) 0462 { 0463 m_blockArray.setHistorySize(size); // nb. of lines. 0464 } 0465 0466 HistoryScrollBlockArray::~HistoryScrollBlockArray() 0467 { 0468 } 0469 0470 int HistoryScrollBlockArray::getLines() 0471 { 0472 return m_lineLengths.size(); 0473 } 0474 0475 int HistoryScrollBlockArray::getLineLen(int lineno) 0476 { 0477 if (m_lineLengths.contains(lineno)) 0478 return m_lineLengths[lineno]; 0479 else 0480 return 0; 0481 } 0482 0483 bool HistoryScrollBlockArray::isWrappedLine(int /*lineno*/) 0484 { 0485 return false; 0486 } 0487 0488 void HistoryScrollBlockArray::getCells(int lineno, int colno, int count, std::span<Character> res) 0489 { 0490 if (!count) 0491 return; 0492 0493 const Block *b = m_blockArray.at(lineno); 0494 0495 if (!b) { 0496 memset(static_cast<void *>(res.data()), 0, count * sizeof(Character)); // still better than random data 0497 return; 0498 } 0499 0500 Q_ASSERT(((colno + count) * sizeof(Character)) < ENTRIES); 0501 memcpy(res.data(), b->data + (colno * sizeof(Character)), count * sizeof(Character)); 0502 } 0503 0504 void HistoryScrollBlockArray::addCells(std::span<const Character> a, int count) 0505 { 0506 Block *b = m_blockArray.lastBlock(); 0507 0508 if (!b) 0509 return; 0510 0511 // put cells in block's data 0512 Q_ASSERT((count * sizeof(Character)) < ENTRIES); 0513 0514 memset(b->data, 0, sizeof(b->data)); 0515 0516 memcpy(b->data, a.data(), count * sizeof(Character)); 0517 b->size = count * sizeof(Character); 0518 0519 size_t res = m_blockArray.newBlock(); 0520 Q_ASSERT(res > 0); 0521 Q_UNUSED(res); 0522 0523 m_lineLengths.insert(m_blockArray.getCurrent(), count); 0524 } 0525 0526 void HistoryScrollBlockArray::addLine(bool) 0527 { 0528 } 0529 0530 //////////////////////////////////////////////////////////////// 0531 // Compact History Scroll ////////////////////////////////////// 0532 //////////////////////////////////////////////////////////////// 0533 void *CompactHistoryBlock::allocate(size_t length) 0534 { 0535 Q_ASSERT(length > 0); 0536 if (tail - blockStart + length > blockLength) 0537 return nullptr; 0538 0539 void *block = tail; 0540 tail += length; 0541 // kDebug() << "allocated " << length << " bytes at address " << block; 0542 allocCount++; 0543 return block; 0544 } 0545 0546 void CompactHistoryBlock::deallocate() 0547 { 0548 allocCount--; 0549 Q_ASSERT(allocCount >= 0); 0550 } 0551 0552 void *CompactHistoryBlockList::allocate(size_t size) 0553 { 0554 CompactHistoryBlock *block; 0555 if (list.isEmpty() || list.last()->remaining() < size) { 0556 block = new CompactHistoryBlock(); 0557 list.append(block); 0558 // kDebug() << "new block created, remaining " << block->remaining() << "number of blocks=" << list.size(); 0559 } else { 0560 block = list.last(); 0561 // kDebug() << "old block used, remaining " << block->remaining(); 0562 } 0563 return block->allocate(size); 0564 } 0565 0566 void CompactHistoryBlockList::deallocate(void *ptr) 0567 { 0568 Q_ASSERT(!list.isEmpty()); 0569 0570 int i = 0; 0571 CompactHistoryBlock *block = list.at(i); 0572 while (i < list.size() && !block->contains(ptr)) { 0573 i++; 0574 block = list.at(i); 0575 } 0576 0577 Q_ASSERT(i < list.size()); 0578 0579 block->deallocate(); 0580 0581 if (!block->isInUse()) { 0582 list.removeAt(i); 0583 delete block; 0584 // kDebug() << "block deleted, new size = " << list.size(); 0585 } 0586 } 0587 0588 CompactHistoryBlockList::~CompactHistoryBlockList() 0589 { 0590 qDeleteAll(list.begin(), list.end()); 0591 list.clear(); 0592 } 0593 0594 void *CompactHistoryLine::operator new(size_t size, CompactHistoryBlockList &blockList) 0595 { 0596 return blockList.allocate(size); 0597 } 0598 0599 CompactHistoryLine::CompactHistoryLine(const TextLine &line, CompactHistoryBlockList &bList) 0600 : blockList(bList) 0601 , formatLength(0) 0602 { 0603 length = line.size(); 0604 0605 if (!line.empty()) { 0606 formatLength = 1; 0607 int k = 1; 0608 0609 // count number of different formats in this text line 0610 Character c = line[0]; 0611 while (k < length) { 0612 if (!(line[k].equalsFormat(c))) { 0613 formatLength++; // format change detected 0614 c = line[k]; 0615 } 0616 k++; 0617 } 0618 0619 // kDebug() << "number of different formats in string: " << formatLength; 0620 formatArray = (CharacterFormat *)blockList.allocate(sizeof(CharacterFormat) * formatLength); 0621 Q_ASSERT(formatArray != nullptr); 0622 text = (quint16 *)blockList.allocate(sizeof(quint16) * line.size()); 0623 Q_ASSERT(text != nullptr); 0624 0625 length = line.size(); 0626 wrapped = false; 0627 0628 // record formats and their positions in the format array 0629 c = line[0]; 0630 formatArray[0].setFormat(c); 0631 formatArray[0].startPos = 0; // there's always at least 1 format (for the entire line, unless a change happens) 0632 0633 k = 1; // look for possible format changes 0634 int j = 1; 0635 while (k < length && j < formatLength) { 0636 if (!(line[k].equalsFormat(c))) { 0637 c = line[k]; 0638 formatArray[j].setFormat(c); 0639 formatArray[j].startPos = k; 0640 // kDebug() << "format entry " << j << " at pos " << formatArray[j].startPos << " " << &(formatArray[j].startPos) ; 0641 j++; 0642 } 0643 k++; 0644 } 0645 0646 // copy character values 0647 for (int i = 0; i < line.size(); i++) { 0648 text[i] = line[i].character.unicode(); 0649 // kDebug() << "char " << i << " at mem " << &(text[i]); 0650 } 0651 } 0652 // kDebug() << "line created, length " << length << " at " << &(length); 0653 } 0654 0655 CompactHistoryLine::~CompactHistoryLine() 0656 { 0657 // kDebug() << "~CHL"; 0658 if (length > 0) { 0659 blockList.deallocate(text); 0660 blockList.deallocate(formatArray); 0661 } 0662 blockList.deallocate(this); 0663 } 0664 0665 void CompactHistoryLine::getCharacter(int index, Character &r) 0666 { 0667 Q_ASSERT(index < length); 0668 int formatPos = 0; 0669 while ((formatPos + 1) < formatLength && index >= formatArray[formatPos + 1].startPos) 0670 formatPos++; 0671 0672 r.character = text[index]; 0673 r.rendition = formatArray[formatPos].rendition; 0674 r.foregroundColor = formatArray[formatPos].fgColor; 0675 r.backgroundColor = formatArray[formatPos].bgColor; 0676 } 0677 0678 void CompactHistoryLine::getCharacters(Character *array, int length, int startColumn) 0679 { 0680 Q_ASSERT(startColumn >= 0 && length >= 0); 0681 Q_ASSERT(startColumn + length <= (int)getLength()); 0682 0683 for (int i = startColumn; i < length + startColumn; i++) { 0684 getCharacter(i, array[i - startColumn]); 0685 } 0686 } 0687 0688 CompactHistoryScroll::CompactHistoryScroll(unsigned int maxLineCount) 0689 : HistoryScroll(new CompactHistoryType(maxLineCount)) 0690 , lines() 0691 , blockList() 0692 { 0693 // kDebug() << "scroll of length " << maxLineCount << " created"; 0694 setMaxNbLines(maxLineCount); 0695 } 0696 0697 CompactHistoryScroll::~CompactHistoryScroll() 0698 { 0699 qDeleteAll(lines.begin(), lines.end()); 0700 lines.clear(); 0701 } 0702 0703 void CompactHistoryScroll::addCellsVector(const TextLine &cells) 0704 { 0705 CompactHistoryLine *line; 0706 line = new (blockList) CompactHistoryLine(cells, blockList); 0707 0708 if (lines.size() > (int)_maxLineCount) { 0709 delete lines.takeAt(0); 0710 } 0711 lines.append(line); 0712 } 0713 0714 void CompactHistoryScroll::addCells(std::span<const Character> a, int count) 0715 { 0716 TextLine newLine(count); 0717 std::copy(a.data(), a.subspan(count).data(), newLine.begin()); 0718 addCellsVector(newLine); 0719 } 0720 0721 void CompactHistoryScroll::addLine(bool previousWrapped) 0722 { 0723 CompactHistoryLine *line = lines.last(); 0724 // kDebug() << "last line at address " << line; 0725 line->setWrapped(previousWrapped); 0726 } 0727 0728 int CompactHistoryScroll::getLines() 0729 { 0730 return lines.size(); 0731 } 0732 0733 int CompactHistoryScroll::getLineLen(int lineNumber) 0734 { 0735 Q_ASSERT(lineNumber >= 0 && lineNumber < lines.size()); 0736 CompactHistoryLine *line = lines[lineNumber]; 0737 // kDebug() << "request for line at address " << line; 0738 return line->getLength(); 0739 } 0740 0741 void CompactHistoryScroll::getCells(int lineNumber, int startColumn, int count, std::span<Character> buffer) 0742 { 0743 if (count == 0) 0744 return; 0745 Q_ASSERT(lineNumber < lines.size()); 0746 CompactHistoryLine *line = lines[lineNumber]; 0747 Q_ASSERT(startColumn >= 0); 0748 Q_ASSERT((unsigned int)startColumn <= line->getLength() - count); 0749 line->getCharacters(buffer.data(), count, startColumn); 0750 } 0751 0752 void CompactHistoryScroll::setMaxNbLines(unsigned int lineCount) 0753 { 0754 _maxLineCount = lineCount; 0755 0756 while (lines.size() > (int)lineCount) { 0757 delete lines.takeAt(0); 0758 } 0759 // kDebug() << "set max lines to: " << _maxLineCount; 0760 } 0761 0762 bool CompactHistoryScroll::isWrappedLine(int lineNumber) 0763 { 0764 Q_ASSERT(lineNumber < lines.size()); 0765 return lines[lineNumber]->isWrapped(); 0766 } 0767 0768 ////////////////////////////////////////////////////////////////////// 0769 // History Types 0770 ////////////////////////////////////////////////////////////////////// 0771 0772 HistoryType::HistoryType() 0773 { 0774 } 0775 0776 HistoryType::~HistoryType() 0777 { 0778 } 0779 0780 ////////////////////////////// 0781 0782 HistoryTypeNone::HistoryTypeNone() 0783 { 0784 } 0785 0786 bool HistoryTypeNone::isEnabled() const 0787 { 0788 return false; 0789 } 0790 0791 std::unique_ptr<HistoryScroll> HistoryTypeNone::scroll(std::unique_ptr<HistoryScroll> &&) const 0792 { 0793 return std::make_unique<HistoryScrollNone>(); 0794 } 0795 0796 int HistoryTypeNone::maximumLineCount() const 0797 { 0798 return 0; 0799 } 0800 0801 ////////////////////////////// 0802 0803 HistoryTypeBlockArray::HistoryTypeBlockArray(size_t size) 0804 : m_size(size) 0805 { 0806 } 0807 0808 bool HistoryTypeBlockArray::isEnabled() const 0809 { 0810 return true; 0811 } 0812 0813 int HistoryTypeBlockArray::maximumLineCount() const 0814 { 0815 return m_size; 0816 } 0817 0818 std::unique_ptr<HistoryScroll> HistoryTypeBlockArray::scroll(std::unique_ptr<HistoryScroll> &&) const 0819 { 0820 return std::make_unique<HistoryScrollBlockArray>(m_size); 0821 } 0822 0823 ////////////////////////////// 0824 0825 HistoryTypeBuffer::HistoryTypeBuffer(unsigned int nbLines) 0826 : m_nbLines(nbLines) 0827 { 0828 } 0829 0830 bool HistoryTypeBuffer::isEnabled() const 0831 { 0832 return true; 0833 } 0834 0835 int HistoryTypeBuffer::maximumLineCount() const 0836 { 0837 return m_nbLines; 0838 } 0839 0840 std::unique_ptr<HistoryScroll> HistoryTypeBuffer::scroll(std::unique_ptr<HistoryScroll> &&old) const 0841 { 0842 if (old) { 0843 HistoryScrollBuffer *oldBuffer = dynamic_cast<HistoryScrollBuffer *>(old.get()); 0844 if (oldBuffer) { 0845 oldBuffer->setMaxNbLines(m_nbLines); 0846 return old; 0847 } 0848 0849 auto newScroll = std::make_unique<HistoryScrollBuffer>(m_nbLines); 0850 int lines = old->getLines(); 0851 int startLine = 0; 0852 if (lines > (int)m_nbLines) 0853 startLine = lines - m_nbLines; 0854 0855 Character line[LINE_SIZE]; 0856 for (int i = startLine; i < lines; i++) { 0857 int size = old->getLineLen(i); 0858 if (size > LINE_SIZE) { 0859 auto tmp_line = std::vector<Character>(size); 0860 old->getCells(i, 0, size, tmp_line); 0861 newScroll->addCells(tmp_line, size); 0862 newScroll->addLine(old->isWrappedLine(i)); 0863 } else { 0864 old->getCells(i, 0, size, line); 0865 newScroll->addCells(line, size); 0866 newScroll->addLine(old->isWrappedLine(i)); 0867 } 0868 } 0869 return newScroll; 0870 } 0871 return std::make_unique<HistoryScrollBuffer>(m_nbLines); 0872 } 0873 0874 ////////////////////////////// 0875 0876 HistoryTypeFile::HistoryTypeFile(const QString &fileName) 0877 : m_fileName(fileName) 0878 { 0879 } 0880 0881 bool HistoryTypeFile::isEnabled() const 0882 { 0883 return true; 0884 } 0885 0886 const QString &HistoryTypeFile::getFileName() const 0887 { 0888 return m_fileName; 0889 } 0890 0891 std::unique_ptr<HistoryScroll> HistoryTypeFile::scroll(std::unique_ptr<HistoryScroll> &&old) const 0892 { 0893 if (dynamic_cast<HistoryFile *>(old.get())) 0894 return old; // Unchanged. 0895 0896 auto newScroll = std::make_unique<HistoryScrollFile>(m_fileName); 0897 0898 Character line[LINE_SIZE]; 0899 int lines = (old != nullptr) ? old->getLines() : 0; 0900 for (int i = 0; i < lines; i++) { 0901 int size = old->getLineLen(i); 0902 if (size > LINE_SIZE) { 0903 auto tmp_line = std::vector<Character>(size); 0904 old->getCells(i, 0, size, tmp_line); 0905 newScroll->addCells(tmp_line, size); 0906 newScroll->addLine(old->isWrappedLine(i)); 0907 } else { 0908 old->getCells(i, 0, size, line); 0909 newScroll->addCells(line, size); 0910 newScroll->addLine(old->isWrappedLine(i)); 0911 } 0912 } 0913 0914 return newScroll; 0915 } 0916 0917 int HistoryTypeFile::maximumLineCount() const 0918 { 0919 return 0; 0920 } 0921 0922 ////////////////////////////// 0923 0924 CompactHistoryType::CompactHistoryType(unsigned int nbLines) 0925 : m_nbLines(nbLines) 0926 { 0927 } 0928 0929 bool CompactHistoryType::isEnabled() const 0930 { 0931 return true; 0932 } 0933 0934 int CompactHistoryType::maximumLineCount() const 0935 { 0936 return m_nbLines; 0937 } 0938 0939 std::unique_ptr<HistoryScroll> CompactHistoryType::scroll(std::unique_ptr<HistoryScroll> &&old) const 0940 { 0941 if (old) { 0942 CompactHistoryScroll *oldBuffer = dynamic_cast<CompactHistoryScroll *>(old.get()); 0943 if (oldBuffer) { 0944 oldBuffer->setMaxNbLines(m_nbLines); 0945 return old; 0946 } 0947 } 0948 return std::make_unique<CompactHistoryScroll>(m_nbLines); 0949 }