diff --git a/src/History.cpp b/src/History.cpp index 0f09ca98..8c70954a 100644 --- a/src/History.cpp +++ b/src/History.cpp @@ -1,703 +1,700 @@ /* This file is part of Konsole, an X terminal. Copyright 1997,1998 by Lars Doelle This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Own #include "History.h" #include "konsoledebug.h" #include "KonsoleSettings.h" // System #include #include #include #include // KDE #include #include #include #include #include // Reasonable line size static const int LINE_SIZE = 1024; using namespace Konsole; Q_GLOBAL_STATIC(QString, historyFileLocation) /* An arbitrary long scroll. One can modify the scroll only by adding either cells or newlines, but access it randomly. The model is that of an arbitrary wide typewriter scroll in that the scroll is a series of lines and each line is a series of cells with no overwriting permitted. The implementation provides arbitrary length and numbers of cells and line/column indexed read access to the scroll at constant costs. */ // History File /////////////////////////////////////////// HistoryFile::HistoryFile() : _length(0), _fileMap(nullptr), _readWriteBalance(0) { // Determine the temp directory once // This class is called 3 times for each "unlimited" scrollback. // This has the down-side that users must restart to // load changes. if (!historyFileLocation.exists()) { QString fileLocation; KSharedConfigPtr appConfig = KSharedConfig::openConfig(); if (qApp->applicationName() != QLatin1String("konsole")) { // Check if "kpart"rc has "FileLocation" group; AFAIK // only possible if user manually added it. If not // found, use konsole's config. if (!appConfig->hasGroup("FileLocation")) { appConfig = KSharedConfig::openConfig(QStringLiteral("konsolerc")); } } KConfigGroup configGroup = appConfig->group("FileLocation"); if (configGroup.readEntry("scrollbackUseCacheLocation", false)) { fileLocation = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); } else if (configGroup.readEntry("scrollbackUseSpecifiedLocation", false)) { const QUrl specifiedUrl = KonsoleSettings::scrollbackUseSpecifiedLocationDirectory(); fileLocation = specifiedUrl.path(); } else { fileLocation = QDir::tempPath(); } // Validate file location const QFileInfo fi(fileLocation); if (fileLocation.isEmpty() || !fi.exists() || !fi.isDir() || !fi.isWritable()) { qCWarning(KonsoleDebug)<<"Invalid scrollback folder "<= _length); _fileMap = _tmpFile.map(0, _length); } //if mmap'ing fails, fall back to the read-lseek combination if (_fileMap == nullptr) { _readWriteBalance = 0; qCDebug(KonsoleDebug) << "mmap'ing history failed. errno = " << errno; } } void HistoryFile::unmap() { Q_ASSERT(_fileMap != nullptr); if (_tmpFile.unmap(_fileMap)) { _fileMap = nullptr; } Q_ASSERT(_fileMap == nullptr); } void HistoryFile::add(const char *buffer, qint64 count) { if (_fileMap != nullptr) { unmap(); } if (_readWriteBalance < INT_MAX) { _readWriteBalance++; } qint64 rc = 0; if (!_tmpFile.seek(_length)) { perror("HistoryFile::add.seek"); return; } rc = _tmpFile.write(buffer, count); if (rc < 0) { perror("HistoryFile::add.write"); return; } _length += rc; } void HistoryFile::get(char *buffer, qint64 size, qint64 loc) { if (loc < 0 || size < 0 || loc + size > _length) { fprintf(stderr, "getHist(...,%lld,%lld): invalid args.\n", size, loc); return; } //count number of get() calls vs. number of add() calls. //If there are many more get() calls compared with add() //calls (decided by using MAP_THRESHOLD) then mmap the log //file to improve performance. if (_readWriteBalance > INT_MIN) { _readWriteBalance--; } if ((_fileMap == nullptr) && _readWriteBalance < MAP_THRESHOLD) { map(); } if (_fileMap != nullptr) { memcpy(buffer, _fileMap + loc, size); } else { qint64 rc = 0; if (!_tmpFile.seek(loc)) { perror("HistoryFile::get.seek"); return; } rc = _tmpFile.read(buffer, size); if (rc < 0) { perror("HistoryFile::get.read"); return; } } } qint64 HistoryFile::len() const { return _length; } // History Scroll abstract base class ////////////////////////////////////// HistoryScroll::HistoryScroll(HistoryType *t) : _historyType(t) { } HistoryScroll::~HistoryScroll() { delete _historyType; } bool HistoryScroll::hasScroll() { return true; } // History Scroll File ////////////////////////////////////// /* The history scroll makes a Row(Row(Cell)) from two history buffers. The index buffer contains start of line positions which refer to the cells buffer. Note that index[0] addresses the second line (line #1), while the first line (line #0) starts at 0 in cells. */ -HistoryScrollFile::HistoryScrollFile(const QString &logFileName) : - HistoryScroll(new HistoryTypeFile(logFileName)) +HistoryScrollFile::HistoryScrollFile() : + HistoryScroll(new HistoryTypeFile()) { } HistoryScrollFile::~HistoryScrollFile() = default; int HistoryScrollFile::getLines() { return _index.len() / sizeof(qint64); } int HistoryScrollFile::getLineLen(int lineno) { return (startOfLine(lineno + 1) - startOfLine(lineno)) / sizeof(Character); } bool HistoryScrollFile::isWrappedLine(int lineno) { if (lineno >= 0 && lineno <= getLines()) { unsigned char flag = 0; _lineflags.get(reinterpret_cast(&flag), sizeof(unsigned char), (lineno)*sizeof(unsigned char)); return flag != 0u; } return false; } qint64 HistoryScrollFile::startOfLine(int lineno) { if (lineno <= 0) { return 0; } if (lineno <= getLines()) { qint64 res = 0; _index.get(reinterpret_cast(&res), sizeof(qint64), (lineno - 1)*sizeof(qint64)); return res; } return _cells.len(); } void HistoryScrollFile::getCells(int lineno, int colno, int count, Character res[]) { _cells.get(reinterpret_cast(res), count * sizeof(Character), startOfLine(lineno) + colno * sizeof(Character)); } void HistoryScrollFile::addCells(const Character text[], int count) { _cells.add(reinterpret_cast(text), count * sizeof(Character)); } void HistoryScrollFile::addLine(bool previousWrapped) { qint64 locn = _cells.len(); _index.add(reinterpret_cast(&locn), sizeof(qint64)); unsigned char flags = previousWrapped ? 0x01 : 0x00; _lineflags.add(reinterpret_cast(&flags), sizeof(char)); } // History Scroll None ////////////////////////////////////// HistoryScrollNone::HistoryScrollNone() : HistoryScroll(new HistoryTypeNone()) { } HistoryScrollNone::~HistoryScrollNone() = default; bool HistoryScrollNone::hasScroll() { return false; } int HistoryScrollNone::getLines() { return 0; } int HistoryScrollNone::getLineLen(int) { return 0; } bool HistoryScrollNone::isWrappedLine(int /*lineno*/) { return false; } void HistoryScrollNone::getCells(int, int, int, Character []) { } void HistoryScrollNone::addCells(const Character [], int) { } void HistoryScrollNone::addLine(bool) { } //////////////////////////////////////////////////////////////// // Compact History Scroll ////////////////////////////////////// //////////////////////////////////////////////////////////////// void *CompactHistoryBlock::allocate(size_t size) { Q_ASSERT(size > 0); if (_tail - _blockStart + size > _blockLength) { return nullptr; } void *block = _tail; _tail += size; ////qDebug() << "allocated " << length << " bytes at address " << block; _allocCount++; return block; } void CompactHistoryBlock::deallocate() { _allocCount--; Q_ASSERT(_allocCount >= 0); } void *CompactHistoryBlockList::allocate(size_t size) { CompactHistoryBlock *block; if (list.isEmpty() || list.last()->remaining() < size) { block = new CompactHistoryBlock(); list.append(block); ////qDebug() << "new block created, remaining " << block->remaining() << "number of blocks=" << list.size(); } else { block = list.last(); ////qDebug() << "old block used, remaining " << block->remaining(); } return block->allocate(size); } void CompactHistoryBlockList::deallocate(void *ptr) { Q_ASSERT(!list.isEmpty()); int i = 0; CompactHistoryBlock *block = list.at(i); while (i < list.size() && !block->contains(ptr)) { i++; block = list.at(i); } Q_ASSERT(i < list.size()); block->deallocate(); if (!block->isInUse()) { list.removeAt(i); delete block; ////qDebug() << "block deleted, new size = " << list.size(); } } CompactHistoryBlockList::~CompactHistoryBlockList() { qDeleteAll(list.begin(), list.end()); list.clear(); } void *CompactHistoryLine::operator new(size_t size, CompactHistoryBlockList &blockList) { return blockList.allocate(size); } CompactHistoryLine::CompactHistoryLine(const TextLine &line, CompactHistoryBlockList &bList) : _blockListRef(bList), _formatArray(nullptr), _text(nullptr), _formatLength(0), _wrapped(false) { _length = line.size(); if (!line.isEmpty()) { _formatLength = 1; int k = 1; // count number of different formats in this text line Character c = line[0]; while (k < _length) { if (!(line[k].equalsFormat(c))) { _formatLength++; // format change detected c = line[k]; } k++; } ////qDebug() << "number of different formats in string: " << _formatLength; _formatArray = static_cast(_blockListRef.allocate(sizeof(CharacterFormat) * _formatLength)); Q_ASSERT(_formatArray != nullptr); _text = static_cast(_blockListRef.allocate(sizeof(uint) * line.size())); Q_ASSERT(_text != nullptr); _length = line.size(); _wrapped = false; // record formats and their positions in the format array c = line[0]; _formatArray[0].setFormat(c); _formatArray[0].startPos = 0; // there's always at least 1 format (for the entire line, unless a change happens) k = 1; // look for possible format changes int j = 1; while (k < _length && j < _formatLength) { if (!(line[k].equalsFormat(c))) { c = line[k]; _formatArray[j].setFormat(c); _formatArray[j].startPos = k; ////qDebug() << "format entry " << j << " at pos " << _formatArray[j].startPos << " " << &(_formatArray[j].startPos) ; j++; } k++; } // copy character values for (int i = 0; i < line.size(); i++) { _text[i] = line[i].character; ////qDebug() << "char " << i << " at mem " << &(text[i]); } } ////qDebug() << "line created, length " << length << " at " << &(length); } CompactHistoryLine::~CompactHistoryLine() { if (_length > 0) { _blockListRef.deallocate(_text); _blockListRef.deallocate(_formatArray); } _blockListRef.deallocate(this); } void CompactHistoryLine::getCharacter(int index, Character &r) { Q_ASSERT(index < _length); int formatPos = 0; while ((formatPos + 1) < _formatLength && index >= _formatArray[formatPos + 1].startPos) { formatPos++; } r.character = _text[index]; r.rendition = _formatArray[formatPos].rendition; r.foregroundColor = _formatArray[formatPos].fgColor; r.backgroundColor = _formatArray[formatPos].bgColor; r.isRealCharacter = _formatArray[formatPos].isRealCharacter; } void CompactHistoryLine::getCharacters(Character *array, int size, int startColumn) { Q_ASSERT(startColumn >= 0 && size >= 0); Q_ASSERT(startColumn + size <= static_cast(getLength())); for (int i = startColumn; i < size + startColumn; i++) { getCharacter(i, array[i - startColumn]); } } CompactHistoryScroll::CompactHistoryScroll(unsigned int maxLineCount) : HistoryScroll(new CompactHistoryType(maxLineCount)), _lines(), _blockList() { ////qDebug() << "scroll of length " << maxLineCount << " created"; setMaxNbLines(maxLineCount); } CompactHistoryScroll::~CompactHistoryScroll() { qDeleteAll(_lines.begin(), _lines.end()); _lines.clear(); } void CompactHistoryScroll::addCellsVector(const TextLine &cells) { CompactHistoryLine *line; line = new(_blockList) CompactHistoryLine(cells, _blockList); if (_lines.size() > static_cast(_maxLineCount)) { delete _lines.takeAt(0); } _lines.append(line); } void CompactHistoryScroll::addCells(const Character a[], int count) { TextLine newLine(count); std::copy(a, a + count, newLine.begin()); addCellsVector(newLine); } void CompactHistoryScroll::addLine(bool previousWrapped) { CompactHistoryLine *line = _lines.last(); ////qDebug() << "last line at address " << line; line->setWrapped(previousWrapped); } int CompactHistoryScroll::getLines() { return _lines.size(); } int CompactHistoryScroll::getLineLen(int lineNumber) { if ((lineNumber < 0) || (lineNumber >= _lines.size())) { //qDebug() << "requested line invalid: 0 < " << lineNumber << " < " <<_lines.size(); //Q_ASSERT(lineNumber >= 0 && lineNumber < _lines.size()); return 0; } CompactHistoryLine *line = _lines[lineNumber]; ////qDebug() << "request for line at address " << line; return line->getLength(); } void CompactHistoryScroll::getCells(int lineNumber, int startColumn, int count, Character buffer[]) { if (count == 0) { return; } Q_ASSERT(lineNumber < _lines.size()); CompactHistoryLine *line = _lines[lineNumber]; Q_ASSERT(startColumn >= 0); Q_ASSERT(static_cast(startColumn) <= line->getLength() - count); line->getCharacters(buffer, count, startColumn); } void CompactHistoryScroll::setMaxNbLines(unsigned int lineCount) { _maxLineCount = lineCount; while (_lines.size() > static_cast(lineCount)) { delete _lines.takeAt(0); } ////qDebug() << "set max lines to: " << _maxLineCount; } bool CompactHistoryScroll::isWrappedLine(int lineNumber) { Q_ASSERT(lineNumber < _lines.size()); return _lines[lineNumber]->isWrapped(); } ////////////////////////////////////////////////////////////////////// // History Types ////////////////////////////////////////////////////////////////////// HistoryType::HistoryType() = default; HistoryType::~HistoryType() = default; ////////////////////////////// HistoryTypeNone::HistoryTypeNone() = default; bool HistoryTypeNone::isEnabled() const { return false; } HistoryScroll *HistoryTypeNone::scroll(HistoryScroll *old) const { delete old; return new HistoryScrollNone(); } int HistoryTypeNone::maximumLineCount() const { return 0; } ////////////////////////////// -HistoryTypeFile::HistoryTypeFile(const QString &fileName) : - _fileName(fileName) -{ -} +HistoryTypeFile::HistoryTypeFile() = default; bool HistoryTypeFile::isEnabled() const { return true; } HistoryScroll *HistoryTypeFile::scroll(HistoryScroll *old) const { if (dynamic_cast(old) != nullptr) { return old; // Unchanged. } - HistoryScroll *newScroll = new HistoryScrollFile(_fileName); + HistoryScroll *newScroll = new HistoryScrollFile(); Character line[LINE_SIZE]; int lines = (old != nullptr) ? old->getLines() : 0; for (int i = 0; i < lines; i++) { int size = old->getLineLen(i); if (size > LINE_SIZE) { auto tmp_line = new Character[size]; old->getCells(i, 0, size, tmp_line); newScroll->addCells(tmp_line, size); newScroll->addLine(old->isWrappedLine(i)); delete [] tmp_line; } else { old->getCells(i, 0, size, line); newScroll->addCells(line, size); newScroll->addLine(old->isWrappedLine(i)); } } delete old; return newScroll; } int HistoryTypeFile::maximumLineCount() const { return -1; } ////////////////////////////// CompactHistoryType::CompactHistoryType(unsigned int nbLines) : _maxLines(nbLines) { } bool CompactHistoryType::isEnabled() const { return true; } int CompactHistoryType::maximumLineCount() const { return _maxLines; } HistoryScroll *CompactHistoryType::scroll(HistoryScroll *old) const { if (old != nullptr) { auto *oldBuffer = dynamic_cast(old); if (oldBuffer != nullptr) { oldBuffer->setMaxNbLines(_maxLines); return oldBuffer; } delete old; } return new CompactHistoryScroll(_maxLines); } diff --git a/src/History.h b/src/History.h index a1e2b932..d89297d4 100644 --- a/src/History.h +++ b/src/History.h @@ -1,413 +1,410 @@ /* This file is part of Konsole, an X terminal. Copyright 1997,1998 by Lars Doelle This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef HISTORY_H #define HISTORY_H // System #include // Qt #include #include #include #include "konsoleprivate_export.h" // Konsole #include "Character.h" namespace Konsole { /* An extendable tmpfile(1) based buffer. */ class HistoryFile { public: HistoryFile(); virtual ~HistoryFile(); virtual void add(const char *buffer, qint64 count); virtual void get(char *buffer, qint64 size, qint64 loc); virtual qint64 len() const; //mmaps the file in read-only mode void map(); //un-mmaps the file void unmap(); private: qint64 _length; QTemporaryFile _tmpFile; //pointer to start of mmap'ed file data, or 0 if the file is not mmap'ed uchar *_fileMap; //incremented whenever 'add' is called and decremented whenever //'get' is called. //this is used to detect when a large number of lines are being read and processed from the history //and automatically mmap the file for better performance (saves the overhead of many lseek-read calls). int _readWriteBalance; //when _readWriteBalance goes below this threshold, the file will be mmap'ed automatically static const int MAP_THRESHOLD = -1000; }; ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // Abstract base class for file and buffer versions ////////////////////////////////////////////////////////////////////// class HistoryType; class KONSOLEPRIVATE_EXPORT HistoryScroll { public: explicit HistoryScroll(HistoryType *); virtual ~HistoryScroll(); virtual bool hasScroll(); // access to history virtual int getLines() = 0; virtual int getLineLen(int lineno) = 0; virtual void getCells(int lineno, int colno, int count, Character res[]) = 0; virtual bool isWrappedLine(int lineNumber) = 0; // adding lines. virtual void addCells(const Character a[], int count) = 0; // convenience method - this is virtual so that subclasses can take advantage // of QVector's implicit copying virtual void addCellsVector(const QVector &cells) { addCells(cells.data(), cells.size()); } virtual void addLine(bool previousWrapped = false) = 0; // // FIXME: Passing around constant references to HistoryType instances // is very unsafe, because those references will no longer // be valid if the history scroll is deleted. // const HistoryType &getType() const { return *_historyType; } protected: HistoryType *_historyType; }; ////////////////////////////////////////////////////////////////////// // File-based history (e.g. file log, no limitation in length) ////////////////////////////////////////////////////////////////////// class KONSOLEPRIVATE_EXPORT HistoryScrollFile : public HistoryScroll { public: - explicit HistoryScrollFile(const QString &logFileName); + explicit HistoryScrollFile(); ~HistoryScrollFile() Q_DECL_OVERRIDE; int getLines() Q_DECL_OVERRIDE; int getLineLen(int lineno) Q_DECL_OVERRIDE; void getCells(int lineno, int colno, int count, Character res[]) Q_DECL_OVERRIDE; bool isWrappedLine(int lineno) Q_DECL_OVERRIDE; void addCells(const Character text[], int count) Q_DECL_OVERRIDE; void addLine(bool previousWrapped = false) Q_DECL_OVERRIDE; private: qint64 startOfLine(int lineno); HistoryFile _index; // lines Row(qint64) HistoryFile _cells; // text Row(Character) HistoryFile _lineflags; // flags Row(unsigned char) }; ////////////////////////////////////////////////////////////////////// // Nothing-based history (no history :-) ////////////////////////////////////////////////////////////////////// class KONSOLEPRIVATE_EXPORT HistoryScrollNone : public HistoryScroll { public: HistoryScrollNone(); ~HistoryScrollNone() Q_DECL_OVERRIDE; bool hasScroll() Q_DECL_OVERRIDE; int getLines() Q_DECL_OVERRIDE; int getLineLen(int lineno) Q_DECL_OVERRIDE; void getCells(int lineno, int colno, int count, Character res[]) Q_DECL_OVERRIDE; bool isWrappedLine(int lineno) Q_DECL_OVERRIDE; void addCells(const Character a[], int count) Q_DECL_OVERRIDE; void addLine(bool previousWrapped = false) Q_DECL_OVERRIDE; }; ////////////////////////////////////////////////////////////////////// // History using compact storage // This implementation uses a list of fixed-sized blocks // where history lines are allocated in (avoids heap fragmentation) ////////////////////////////////////////////////////////////////////// typedef QVector TextLine; class CharacterFormat { public: bool equalsFormat(const CharacterFormat &other) const { return (other.rendition & ~RE_EXTENDED_CHAR) == (rendition & ~RE_EXTENDED_CHAR) && other.fgColor == fgColor && other.bgColor == bgColor; } bool equalsFormat(const Character &c) const { return (c.rendition & ~RE_EXTENDED_CHAR) == (rendition & ~RE_EXTENDED_CHAR) && c.foregroundColor == fgColor && c.backgroundColor == bgColor; } void setFormat(const Character &c) { rendition = c.rendition; fgColor = c.foregroundColor; bgColor = c.backgroundColor; isRealCharacter = c.isRealCharacter; } CharacterColor fgColor, bgColor; quint16 startPos; RenditionFlags rendition; bool isRealCharacter; }; class CompactHistoryBlock { public: CompactHistoryBlock() : _blockLength(4096 * 64), // 256kb _head(static_cast(mmap(nullptr, _blockLength, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0))), _tail(nullptr), _blockStart(nullptr), _allocCount(0) { Q_ASSERT(_head != MAP_FAILED); _tail = _blockStart = _head; } virtual ~CompactHistoryBlock() { //free(_blockStart); munmap(_blockStart, _blockLength); } virtual unsigned int remaining() { return _blockStart + _blockLength - _tail; } virtual unsigned length() { return _blockLength; } virtual void *allocate(size_t size); virtual bool contains(void *addr) { return addr >= _blockStart && addr < (_blockStart + _blockLength); } virtual void deallocate(); virtual bool isInUse() { return _allocCount != 0; } private: size_t _blockLength; quint8 *_head; quint8 *_tail; quint8 *_blockStart; int _allocCount; }; class CompactHistoryBlockList { public: CompactHistoryBlockList() : list(QList()) { } ~CompactHistoryBlockList(); void *allocate(size_t size); void deallocate(void *); int length() { return list.size(); } private: QList list; }; class CompactHistoryLine { public: CompactHistoryLine(const TextLine &, CompactHistoryBlockList &blockList); virtual ~CompactHistoryLine(); // custom new operator to allocate memory from custom pool instead of heap static void *operator new(size_t size, CompactHistoryBlockList &blockList); static void operator delete(void *) { /* do nothing, deallocation from pool is done in destructor*/ } virtual void getCharacters(Character *array, int size, int startColumn); virtual void getCharacter(int index, Character &r); virtual bool isWrapped() const { return _wrapped; } virtual void setWrapped(bool value) { _wrapped = value; } virtual unsigned int getLength() const { return _length; } protected: CompactHistoryBlockList &_blockListRef; CharacterFormat *_formatArray; quint16 _length; uint *_text; quint16 _formatLength; bool _wrapped; }; class KONSOLEPRIVATE_EXPORT CompactHistoryScroll : public HistoryScroll { typedef QList HistoryArray; public: explicit CompactHistoryScroll(unsigned int maxLineCount = 1000); ~CompactHistoryScroll() Q_DECL_OVERRIDE; int getLines() Q_DECL_OVERRIDE; int getLineLen(int lineNumber) Q_DECL_OVERRIDE; void getCells(int lineNumber, int startColumn, int count, Character buffer[]) Q_DECL_OVERRIDE; bool isWrappedLine(int lineNumber) Q_DECL_OVERRIDE; void addCells(const Character a[], int count) Q_DECL_OVERRIDE; void addCellsVector(const TextLine &cells) Q_DECL_OVERRIDE; void addLine(bool previousWrapped = false) Q_DECL_OVERRIDE; void setMaxNbLines(unsigned int lineCount); private: bool hasDifferentColors(const TextLine &line) const; HistoryArray _lines; CompactHistoryBlockList _blockList; unsigned int _maxLineCount; }; ////////////////////////////////////////////////////////////////////// // History type ////////////////////////////////////////////////////////////////////// class KONSOLEPRIVATE_EXPORT HistoryType { public: HistoryType(); virtual ~HistoryType(); /** * Returns true if the history is enabled ( can store lines of output ) * or false otherwise. */ virtual bool isEnabled() const = 0; /** * Returns the maximum number of lines which this history type * can store or -1 if the history can store an unlimited number of lines. */ virtual int maximumLineCount() const = 0; /** * Converts from one type of HistoryScroll to another or if given the * same type, returns it. */ virtual HistoryScroll *scroll(HistoryScroll *) const = 0; /** * Returns true if the history size is unlimited. */ bool isUnlimited() const { return maximumLineCount() == -1; } }; class KONSOLEPRIVATE_EXPORT HistoryTypeNone : public HistoryType { public: HistoryTypeNone(); bool isEnabled() const Q_DECL_OVERRIDE; int maximumLineCount() const Q_DECL_OVERRIDE; HistoryScroll *scroll(HistoryScroll *) const Q_DECL_OVERRIDE; }; class KONSOLEPRIVATE_EXPORT HistoryTypeFile : public HistoryType { public: - explicit HistoryTypeFile(const QString &fileName = QString()); + explicit HistoryTypeFile(); bool isEnabled() const Q_DECL_OVERRIDE; int maximumLineCount() const Q_DECL_OVERRIDE; HistoryScroll *scroll(HistoryScroll *) const Q_DECL_OVERRIDE; - -protected: - QString _fileName; }; class KONSOLEPRIVATE_EXPORT CompactHistoryType : public HistoryType { public: explicit CompactHistoryType(unsigned int nbLines); bool isEnabled() const Q_DECL_OVERRIDE; int maximumLineCount() const Q_DECL_OVERRIDE; HistoryScroll *scroll(HistoryScroll *) const Q_DECL_OVERRIDE; protected: unsigned int _maxLines; }; } #endif // HISTORY_H diff --git a/src/autotests/HistoryTest.cpp b/src/autotests/HistoryTest.cpp index ae729fd2..98b38f1d 100644 --- a/src/autotests/HistoryTest.cpp +++ b/src/autotests/HistoryTest.cpp @@ -1,143 +1,143 @@ /* Copyright 2013 by Kurt Hindenburg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Own #include "HistoryTest.h" #include "qtest.h" // Konsole #include "../Session.h" #include "../Emulation.h" #include "../History.h" using namespace Konsole; void HistoryTest::testHistoryNone() { HistoryType *history; history = new HistoryTypeNone(); QCOMPARE(history->isEnabled(), false); QCOMPARE(history->isUnlimited(), false); QCOMPARE(history->maximumLineCount(), 0); delete history; } void HistoryTest::testHistoryFile() { HistoryType *history; history = new HistoryTypeFile(); QCOMPARE(history->isEnabled(), true); QCOMPARE(history->isUnlimited(), true); QCOMPARE(history->maximumLineCount(), -1); delete history; } void HistoryTest::testCompactHistory() { HistoryType *history; history = new CompactHistoryType(42); QCOMPARE(history->isEnabled(), true); QCOMPARE(history->isUnlimited(), false); QCOMPARE(history->maximumLineCount(), 42); delete history; } void HistoryTest::testEmulationHistory() { auto session = new Session(); Emulation *emulation = session->emulation(); const HistoryType &historyTypeDefault = emulation->history(); QCOMPARE(historyTypeDefault.isEnabled(), false); QCOMPARE(historyTypeDefault.isUnlimited(), false); QCOMPARE(historyTypeDefault.maximumLineCount(), 0); emulation->setHistory(HistoryTypeNone()); const HistoryType &historyTypeNone = emulation->history(); QCOMPARE(historyTypeNone.isEnabled(), false); QCOMPARE(historyTypeNone.isUnlimited(), false); QCOMPARE(historyTypeNone.maximumLineCount(), 0); emulation->setHistory(HistoryTypeFile()); const HistoryType &historyTypeFile = emulation->history(); QCOMPARE(historyTypeFile.isEnabled(), true); QCOMPARE(historyTypeFile.isUnlimited(), true); QCOMPARE(historyTypeFile.maximumLineCount(), -1); emulation->setHistory(CompactHistoryType(42)); const HistoryType &compactHistoryType = emulation->history(); QCOMPARE(compactHistoryType.isEnabled(), true); QCOMPARE(compactHistoryType.isUnlimited(), false); QCOMPARE(compactHistoryType.maximumLineCount(), 42); delete session; } void HistoryTest::testHistoryScroll() { HistoryScroll *historyScroll; // None historyScroll = new HistoryScrollNone(); QVERIFY(!historyScroll->hasScroll()); QCOMPARE(historyScroll->getLines(), 0); QCOMPARE(historyScroll->getLineLen(0), 0); QCOMPARE(historyScroll->getLineLen(10), 0); const HistoryType &historyTypeNone = historyScroll->getType(); QCOMPARE(historyTypeNone.isEnabled(), false); QCOMPARE(historyTypeNone.isUnlimited(), false); QCOMPARE(historyTypeNone.maximumLineCount(), 0); delete historyScroll; // File - historyScroll = new HistoryScrollFile(QStringLiteral("test.log")); + historyScroll = new HistoryScrollFile(); QVERIFY(historyScroll->hasScroll()); QCOMPARE(historyScroll->getLines(), 0); QCOMPARE(historyScroll->getLineLen(0), 0); QCOMPARE(historyScroll->getLineLen(10), 0); const HistoryType &historyTypeFile = historyScroll->getType(); QCOMPARE(historyTypeFile.isEnabled(), true); QCOMPARE(historyTypeFile.isUnlimited(), true); QCOMPARE(historyTypeFile.maximumLineCount(), -1); delete historyScroll; // Compact historyScroll = new CompactHistoryScroll(42); QVERIFY(historyScroll->hasScroll()); QCOMPARE(historyScroll->getLines(), 0); QCOMPARE(historyScroll->getLineLen(0), 0); QCOMPARE(historyScroll->getLineLen(10), 0); const HistoryType &compactHistoryType = historyScroll->getType(); QCOMPARE(compactHistoryType.isEnabled(), true); QCOMPARE(compactHistoryType.isUnlimited(), false); QCOMPARE(compactHistoryType.maximumLineCount(), 42); delete historyScroll; } QTEST_MAIN(HistoryTest)