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"