File indexing completed on 2025-04-27 05:00:42
0001 /* 0002 SPDX-FileCopyrightText: 2007 Robert Knight <robertknight@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 0006 This program is distributed in the hope that it will be useful, 0007 but WITHOUT ANY WARRANTY; without even the implied warranty of 0008 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0009 GNU General Public License for more details. 0010 0011 You should have received a copy of the GNU General Public License 0012 along with this program; if not, write to the Free Software 0013 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0014 02110-1301 USA. 0015 */ 0016 0017 // Own 0018 #include "ScreenWindow.h" 0019 0020 // Qt 0021 #include <QtDebug> 0022 0023 // Konsole 0024 #include "Screen.h" 0025 0026 using namespace Konsole; 0027 0028 ScreenWindow::ScreenWindow(QObject *parent) 0029 : QObject(parent) 0030 , _screen(nullptr) 0031 , _windowBufferSize(0) 0032 , _bufferNeedsUpdate(true) 0033 , _windowLines(1) 0034 , _currentLine(0) 0035 , _trackOutput(true) 0036 , _scrollCount(0) 0037 { 0038 } 0039 ScreenWindow::~ScreenWindow() = default; 0040 0041 void ScreenWindow::setScreen(Screen *screen) 0042 { 0043 Q_ASSERT(screen); 0044 0045 _screen = screen; 0046 } 0047 0048 Screen *ScreenWindow::screen() const 0049 { 0050 return _screen; 0051 } 0052 0053 std::span<Character> ScreenWindow::getImage() 0054 { 0055 // reallocate internal buffer if the window size has changed 0056 int size = windowLines() * windowColumns(); 0057 if (_windowBuffer.empty() || _windowBufferSize != size) { 0058 _windowBufferSize = size; 0059 _windowBuffer = std::vector<Character>(size); 0060 _bufferNeedsUpdate = true; 0061 } 0062 0063 if (!_bufferNeedsUpdate) 0064 return _windowBuffer; 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(std::span(_windowBuffer).subspan(_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 QVector<LineProperty> ScreenWindow::getLineProperties() 0107 { 0108 QVector<LineProperty> result = _screen->getLineProperties(currentLine(), endWindowLine()); 0109 0110 if (result.size() != windowLines()) 0111 result.resize(windowLines()); 0112 0113 return result; 0114 } 0115 0116 QString ScreenWindow::selectedText(bool preserveLineBreaks) const 0117 { 0118 return _screen->selectedText(preserveLineBreaks); 0119 } 0120 0121 void ScreenWindow::getSelectionStart(int &column, int &line) 0122 { 0123 _screen->getSelectionStart(column, line); 0124 line -= currentLine(); 0125 } 0126 void ScreenWindow::getSelectionEnd(int &column, int &line) 0127 { 0128 _screen->getSelectionEnd(column, line); 0129 line -= currentLine(); 0130 } 0131 void ScreenWindow::setSelectionStart(int column, int line, bool columnMode) 0132 { 0133 _screen->setSelectionStart(column, qMin(line + currentLine(), endWindowLine()), columnMode); 0134 0135 _bufferNeedsUpdate = true; 0136 Q_EMIT selectionChanged(); 0137 } 0138 0139 void ScreenWindow::setSelectionEnd(int column, int line) 0140 { 0141 _screen->setSelectionEnd(column, qMin(line + currentLine(), endWindowLine())); 0142 0143 _bufferNeedsUpdate = true; 0144 Q_EMIT selectionChanged(); 0145 } 0146 0147 bool ScreenWindow::isSelected(int column, int line) 0148 { 0149 return _screen->isSelected(column, qMin(line + currentLine(), endWindowLine())); 0150 } 0151 0152 void ScreenWindow::clearSelection() 0153 { 0154 _screen->clearSelection(); 0155 0156 Q_EMIT selectionChanged(); 0157 } 0158 0159 void ScreenWindow::setWindowLines(int lines) 0160 { 0161 Q_ASSERT(lines > 0); 0162 _windowLines = lines; 0163 } 0164 int ScreenWindow::windowLines() const 0165 { 0166 return _windowLines; 0167 } 0168 0169 int ScreenWindow::windowColumns() const 0170 { 0171 return _screen->getColumns(); 0172 } 0173 0174 int ScreenWindow::lineCount() const 0175 { 0176 return _screen->getHistLines() + _screen->getLines(); 0177 } 0178 0179 int ScreenWindow::columnCount() const 0180 { 0181 return _screen->getColumns(); 0182 } 0183 0184 QPoint ScreenWindow::cursorPosition() const 0185 { 0186 QPoint position; 0187 0188 position.setX(_screen->getCursorX()); 0189 position.setY(_screen->getCursorY()); 0190 0191 return position; 0192 } 0193 0194 int ScreenWindow::currentLine() const 0195 { 0196 return qBound(0, _currentLine, lineCount() - windowLines()); 0197 } 0198 0199 void ScreenWindow::scrollBy(RelativeScrollMode mode, int amount) 0200 { 0201 if (mode == ScrollLines) { 0202 scrollTo(currentLine() + amount); 0203 } else if (mode == ScrollPages) { 0204 scrollTo(currentLine() + amount * (windowLines() / 2)); 0205 } 0206 } 0207 0208 bool ScreenWindow::atEndOfOutput() const 0209 { 0210 return currentLine() == (lineCount() - windowLines()); 0211 } 0212 0213 void ScreenWindow::scrollTo(int line) 0214 { 0215 int maxCurrentLineNumber = lineCount() - windowLines(); 0216 line = qBound(0, line, maxCurrentLineNumber); 0217 0218 const int delta = line - _currentLine; 0219 _currentLine = line; 0220 0221 // keep track of number of lines scrolled by, 0222 // this can be reset by calling resetScrollCount() 0223 _scrollCount += delta; 0224 0225 _bufferNeedsUpdate = true; 0226 0227 Q_EMIT scrolled(_currentLine); 0228 } 0229 0230 void ScreenWindow::setTrackOutput(bool trackOutput) 0231 { 0232 _trackOutput = trackOutput; 0233 } 0234 0235 bool ScreenWindow::trackOutput() const 0236 { 0237 return _trackOutput; 0238 } 0239 0240 int ScreenWindow::scrollCount() const 0241 { 0242 return _scrollCount; 0243 } 0244 0245 void ScreenWindow::resetScrollCount() 0246 { 0247 _scrollCount = 0; 0248 } 0249 0250 QRect ScreenWindow::scrollRegion() const 0251 { 0252 bool equalToScreenSize = windowLines() == _screen->getLines(); 0253 0254 if (atEndOfOutput() && equalToScreenSize) 0255 return _screen->lastScrolledRegion(); 0256 else 0257 return {0, 0, windowColumns(), windowLines()}; 0258 } 0259 0260 void ScreenWindow::notifyOutputChanged() 0261 { 0262 // move window to the bottom of the screen and update scroll count 0263 // if this window is currently tracking the bottom of the screen 0264 if (_trackOutput) { 0265 _scrollCount -= _screen->scrolledLines(); 0266 _currentLine = qMax(0, _screen->getHistLines() - (windowLines() - _screen->getLines())); 0267 } else { 0268 // if the history is not unlimited then it may 0269 // have run out of space and dropped the oldest 0270 // lines of output - in this case the screen 0271 // window's current line number will need to 0272 // be adjusted - otherwise the output will scroll 0273 _currentLine = qMax(0, _currentLine - _screen->droppedLines()); 0274 0275 // ensure that the screen window's current position does 0276 // not go beyond the bottom of the screen 0277 _currentLine = qMin(_currentLine, _screen->getHistLines()); 0278 } 0279 0280 _bufferNeedsUpdate = true; 0281 0282 Q_EMIT outputChanged(); 0283 } 0284 0285 void ScreenWindow::handleCommandFromKeyboard(KeyboardTranslator::Command command) 0286 { 0287 // Keyboard-based navigation 0288 bool update = false; 0289 0290 // EraseCommand is handled in Vt102Emulation 0291 if (command & KeyboardTranslator::ScrollPageUpCommand) { 0292 scrollBy(ScreenWindow::ScrollPages, -1); 0293 update = true; 0294 } 0295 if (command & KeyboardTranslator::ScrollPageDownCommand) { 0296 scrollBy(ScreenWindow::ScrollPages, 1); 0297 update = true; 0298 } 0299 if (command & KeyboardTranslator::ScrollLineUpCommand) { 0300 scrollBy(ScreenWindow::ScrollLines, -1); 0301 update = true; 0302 } 0303 if (command & KeyboardTranslator::ScrollLineDownCommand) { 0304 scrollBy(ScreenWindow::ScrollLines, 1); 0305 update = true; 0306 } 0307 if (command & KeyboardTranslator::ScrollDownToBottomCommand) { 0308 Q_EMIT scrollToEnd(); 0309 update = true; 0310 } 0311 if (command & KeyboardTranslator::ScrollUpToTopCommand) { 0312 scrollTo(0); 0313 update = true; 0314 } 0315 // TODO: KeyboardTranslator::ScrollLockCommand 0316 // TODO: KeyboardTranslator::SendCommand 0317 0318 if (update) { 0319 setTrackOutput(atEndOfOutput()); 0320 0321 Q_EMIT outputChanged(); 0322 } 0323 } 0324 0325 // #include "ScreenWindow.moc"