File indexing completed on 2024-04-14 05:44:13

0001 /*
0002     SPDX-FileCopyrightText: 2007 Robert Knight <robertknight@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 // Own
0008 #include "ScreenWindow.h"
0009 
0010 // Konsole
0011 
0012 using namespace Konsole;
0013 
0014 ScreenWindow::ScreenWindow(Screen *screen, QObject *parent)
0015     : QObject(parent)
0016     , _screen(nullptr)
0017     , _windowBuffer(nullptr)
0018     , _windowBufferSize(0)
0019     , _bufferNeedsUpdate(true)
0020     , _windowLines(1)
0021     , _currentLine(0)
0022     , _currentResultLine(-1)
0023     , _trackOutput(true)
0024     , _scrollCount(0)
0025 {
0026     setScreen(screen);
0027 }
0028 
0029 ScreenWindow::~ScreenWindow()
0030 {
0031     delete[] _windowBuffer;
0032 }
0033 
0034 void ScreenWindow::setScreen(Screen *screen)
0035 {
0036     Q_ASSERT(screen);
0037 
0038     if (screen == _screen) {
0039         return;
0040     }
0041 
0042     Q_EMIT screenAboutToChange();
0043     _screen = screen;
0044 }
0045 
0046 Screen *ScreenWindow::screen() const
0047 {
0048     return _screen;
0049 }
0050 
0051 Character *ScreenWindow::getImage()
0052 {
0053     // reallocate internal buffer if the window size has changed
0054     int size = windowLines() * windowColumns();
0055     if (_windowBuffer == nullptr || _windowBufferSize != size) {
0056         delete[] _windowBuffer;
0057         _windowBufferSize = size;
0058         _windowBuffer = new Character[size];
0059         _bufferNeedsUpdate = true;
0060     }
0061 
0062     if (!_bufferNeedsUpdate) {
0063         return _windowBuffer;
0064     }
0065 
0066     _screen->getImage(_windowBuffer, size, currentLine(), endWindowLine());
0067 
0068     // this window may look beyond the end of the screen, in which
0069     // case there will be an unused area which needs to be filled
0070     // with blank characters
0071     fillUnusedArea();
0072 
0073     _bufferNeedsUpdate = false;
0074     return _windowBuffer;
0075 }
0076 
0077 void ScreenWindow::fillUnusedArea()
0078 {
0079     int screenEndLine = _screen->getHistLines() + _screen->getLines() - 1;
0080     int windowEndLine = currentLine() + windowLines() - 1;
0081 
0082     int unusedLines = windowEndLine - screenEndLine;
0083 
0084     // stop when unusedLines is negative; there is an issue w/ charsToFill
0085     //  being greater than an int can hold
0086     if (unusedLines <= 0) {
0087         return;
0088     }
0089 
0090     int charsToFill = unusedLines * windowColumns();
0091 
0092     Screen::fillWithDefaultChar(_windowBuffer + _windowBufferSize - charsToFill, charsToFill);
0093 }
0094 
0095 // return the index of the line at the end of this window, or if this window
0096 // goes beyond the end of the screen, the index of the line at the end
0097 // of the screen.
0098 //
0099 // when passing a line number to a Screen method, the line number should
0100 // never be more than endWindowLine()
0101 //
0102 int ScreenWindow::endWindowLine() const
0103 {
0104     return qMin(currentLine() + windowLines() - 1, lineCount() - 1);
0105 }
0106 
0107 QVector<LineProperty> ScreenWindow::getLineProperties()
0108 {
0109     QVector<LineProperty> result = _screen->getLineProperties(currentLine(), endWindowLine());
0110 
0111     if (result.count() != windowLines()) {
0112         result.resize(windowLines());
0113     }
0114 
0115     return result;
0116 }
0117 
0118 QString ScreenWindow::selectedText(const Screen::DecodingOptions options) const
0119 {
0120     return _screen->selectedText(options);
0121 }
0122 
0123 void ScreenWindow::getSelectionStart(int &column, int &line)
0124 {
0125     _screen->getSelectionStart(column, line);
0126     line -= currentLine();
0127 }
0128 
0129 void ScreenWindow::getSelectionEnd(int &column, int &line)
0130 {
0131     _screen->getSelectionEnd(column, line);
0132     line -= currentLine();
0133 }
0134 
0135 void ScreenWindow::setSelectionStart(int column, int line, bool columnMode)
0136 {
0137     _screen->setSelectionStart(column, line + currentLine(), columnMode);
0138 
0139     _bufferNeedsUpdate = true;
0140     Q_EMIT selectionChanged();
0141 }
0142 
0143 void ScreenWindow::setSelectionEnd(int column, int line, bool trimTrailingWhitespace)
0144 {
0145     _screen->setSelectionEnd(column, line + currentLine(), trimTrailingWhitespace);
0146 
0147     _bufferNeedsUpdate = true;
0148     Q_EMIT selectionChanged();
0149 }
0150 
0151 void ScreenWindow::setSelectionByLineRange(int start, int end)
0152 {
0153     clearSelection();
0154 
0155     _screen->setSelectionStart(0, start, false);
0156     _screen->setSelectionEnd(windowColumns(), end, false);
0157 
0158     _bufferNeedsUpdate = true;
0159     Q_EMIT selectionChanged();
0160 }
0161 
0162 bool ScreenWindow::isSelected(int column, int line)
0163 {
0164     return _screen->isSelected(column, qMin(line + currentLine(), endWindowLine()));
0165 }
0166 
0167 void ScreenWindow::clearSelection()
0168 {
0169     _screen->clearSelection();
0170 
0171     Q_EMIT selectionChanged();
0172 }
0173 
0174 void ScreenWindow::setWindowLines(int lines)
0175 {
0176     Q_ASSERT(lines > 0);
0177     _windowLines = lines;
0178 }
0179 
0180 int ScreenWindow::windowLines() const
0181 {
0182     return _windowLines;
0183 }
0184 
0185 int ScreenWindow::windowColumns() const
0186 {
0187     return _screen->getColumns();
0188 }
0189 
0190 int ScreenWindow::lineCount() const
0191 {
0192     return _screen->getHistLines() + _screen->getLines();
0193 }
0194 
0195 int ScreenWindow::columnCount() const
0196 {
0197     return _screen->getColumns();
0198 }
0199 
0200 QPoint ScreenWindow::cursorPosition() const
0201 {
0202     QPoint position;
0203 
0204     position.setX(_screen->getCursorX());
0205     position.setY(_screen->getCursorY());
0206 
0207     return position;
0208 }
0209 
0210 int ScreenWindow::currentLine() const
0211 {
0212     return qBound(0, _currentLine, std::max(0, lineCount() - windowLines()));
0213 }
0214 
0215 int ScreenWindow::currentResultLine() const
0216 {
0217     return _currentResultLine;
0218 }
0219 
0220 void ScreenWindow::setCurrentResultLine(int line)
0221 {
0222     if (_currentResultLine == line) {
0223         return;
0224     }
0225 
0226     _currentResultLine = line;
0227     Q_EMIT currentResultLineChanged();
0228 }
0229 
0230 void ScreenWindow::scrollBy(RelativeScrollMode mode, int amount, bool fullPage)
0231 {
0232     if (mode == ScrollLines) {
0233         scrollTo(currentLine() + amount);
0234     } else if (mode == ScrollPages || (mode == ScrollPrompts && !_screen->hasRepl())) {
0235         if (fullPage) {
0236             scrollTo(currentLine() + amount * (windowLines()));
0237         } else {
0238             scrollTo(currentLine() + amount * (windowLines() / 2));
0239         }
0240     } else if (mode == ScrollPrompts) {
0241         int i = currentLine();
0242         if (amount < 0) {
0243             QVector<LineProperty> properties = _screen->getLineProperties(0, currentLine());
0244             while (i > 0 && amount < 0) {
0245                 i--;
0246                 if ((properties[i].flags.f.prompt_start) != 0) {
0247                     if (++amount == 0) {
0248                         break;
0249                     }
0250                 }
0251             }
0252         } else if (amount > 0) {
0253             QVector<LineProperty> properties = _screen->getLineProperties(currentLine(), _screen->getHistLines());
0254             while (i < _screen->getHistLines() && amount > 0) {
0255                 i++;
0256                 if ((properties[i - currentLine()].flags.f.prompt_start) != 0) {
0257                     if (--amount == 0) {
0258                         break;
0259                     }
0260                 }
0261             }
0262         }
0263         scrollTo(i);
0264     }
0265 }
0266 
0267 bool ScreenWindow::atEndOfOutput() const
0268 {
0269     return currentLine() == (lineCount() - windowLines());
0270 }
0271 
0272 void ScreenWindow::scrollTo(int line)
0273 {
0274     int maxCurrentLineNumber = lineCount() - windowLines();
0275     line = qBound(0, line, maxCurrentLineNumber);
0276 
0277     const int delta = line - _currentLine;
0278     _currentLine = line;
0279 
0280     // keep track of number of lines scrolled by,
0281     // this can be reset by calling resetScrollCount()
0282     _scrollCount += delta;
0283 
0284     _bufferNeedsUpdate = true;
0285 
0286     Q_EMIT scrolled(_currentLine);
0287 }
0288 
0289 void ScreenWindow::setTrackOutput(bool trackOutput)
0290 {
0291     _trackOutput = trackOutput;
0292 }
0293 
0294 bool ScreenWindow::trackOutput() const
0295 {
0296     return _trackOutput;
0297 }
0298 
0299 int ScreenWindow::scrollCount() const
0300 {
0301     return _scrollCount;
0302 }
0303 
0304 void ScreenWindow::resetScrollCount()
0305 {
0306     _scrollCount = 0;
0307 }
0308 
0309 QRect ScreenWindow::scrollRegion() const
0310 {
0311     bool equalToScreenSize = windowLines() == _screen->getLines();
0312 
0313     if (atEndOfOutput() && equalToScreenSize) {
0314         return _screen->lastScrolledRegion();
0315     }
0316     return {0, 0, windowColumns(), windowLines()};
0317 }
0318 
0319 void ScreenWindow::updateCurrentLine()
0320 {
0321     if (!_screen->isResize()) {
0322         return;
0323     }
0324     if (_currentLine > 0) {
0325         _currentLine -= _screen->getOldTotalLines() - lineCount();
0326     }
0327     _currentLine = currentLine();
0328 }
0329 
0330 void ScreenWindow::notifyOutputChanged()
0331 {
0332     // move window to the bottom of the screen and update scroll count
0333     // if this window is currently tracking the bottom of the screen
0334     if (_trackOutput) {
0335         _scrollCount -= _screen->scrolledLines();
0336         _currentLine = qMax(0, _screen->getHistLines() - (windowLines() - _screen->getLines()));
0337     } else {
0338         // if the history is not unlimited then it may
0339         // have run out of space and dropped the oldest
0340         // lines of output - in this case the screen
0341         // window's current line number will need to
0342         // be adjusted - otherwise the output will scroll
0343         _currentLine = qMax(0, _currentLine - _screen->droppedLines());
0344 
0345         // ensure that the screen window's current position does
0346         // not go beyond the bottom of the screen
0347         _currentLine = qMin(_currentLine, _screen->getHistLines());
0348     }
0349 
0350     _bufferNeedsUpdate = true;
0351 
0352     Q_EMIT outputChanged();
0353 }
0354 
0355 #include "moc_ScreenWindow.cpp"