21#include "TerminalDisplay.h"
27#include <QAbstractButton>
28#include <QApplication>
39#include <QRegularExpression>
49#include "ScreenWindow.h"
50#include "TerminalCharacterDecoder.h"
51#include "konsole_wcwidth.h"
56inline void initResource()
61using namespace Konsole;
63constexpr auto REPCHAR =
64 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
65 "abcdefgjijklmnopqrstuvwxyz"
71bool TerminalDisplay::_antialiasText =
true;
75const QChar LTR_OVERRIDE_CHAR(0x202D);
99 disconnect(_screenWindow,
nullptr,
this,
nullptr);
109 window->setWindowLines(_lines);
120 _colorTable[DEFAULT_BACK_COLOR].color = color;
122 p.
setColor(backgroundRole(), color);
131QColor TerminalDisplay::backgroundColor()
const
133 return _colorTable[DEFAULT_BACK_COLOR].color;
138 _colorTable[DEFAULT_FORE_COLOR].color = color;
143QColor TerminalDisplay::foregroundColor()
const
145 return _colorTable[DEFAULT_FORE_COLOR].color;
150 _colorTable = std::move(table);
172constexpr bool TerminalDisplay::isLineChar(
QChar c)
const
174 return _drawLineChars && ((c.
unicode() & 0xFF80) == 0x2500);
177constexpr bool TerminalDisplay::isLineCharString(
QStringView string)
const
179 return (
string.size() > 0) && (isLineChar(
string[0]));
182void TerminalDisplay::fontChange(
const QFont &)
184 QFontMetricsF fm(font());
185 _fontHeight = fm.height() + _lineSpacing;
191 _fontWidth = fm.horizontalAdvance(QLatin1String(REPCHAR)) / (qreal)qstrlen(REPCHAR);
195 int fw = fm.horizontalAdvance(QLatin1Char(REPCHAR[0]));
196 for (
unsigned int i = 1; i < qstrlen(REPCHAR); i++) {
197 if (fw != fm.horizontalAdvance(QLatin1Char(REPCHAR[i]))) {
206 _fontAscent = fm.ascent();
208 Q_EMIT changedFontMetricSignal(_fontHeight, _fontWidth);
234 qDebug() <<
"Using a variable-width font in the terminal. This may cause performance degradation and display/alignment errors.";
245 font.setKerning(
false);
254 if (_boldIntense != value) {
255 _boldIntense = value;
256 Q_EMIT boldIntenseChanged();
269 , _screenWindow(nullptr)
271 , _gridLayout(nullptr)
284 , _terminalSizeHint(false)
285 , _terminalSizeStartup(true)
286 , _bidiEnabled(false)
288 , _disabledBracketedPasteMode(false)
290 , _wordSelectionMode(false)
291 , _lineSelectionMode(false)
292 , _preserveLineBreaks(false)
293 , _columnSelectionMode(false)
299 , _cursorBlinking(false)
300 , _hasBlinkingCursor(false)
301 , _allowBlinkingText(true)
304 , _isFixedSize(false)
305 , _possibleTripleClick(false)
306 , _resizeWidget(nullptr)
307 , _resizeTimer(nullptr)
308 , _flowControlWarningEnabled(false)
309 , _outputSuspendedLabel(nullptr)
311 , _colorsInverted(false)
312 , _opacity(static_cast<qreal>(1))
314 , _cursorShape(
Emulation::KeyboardCursorShape::BlockCursor)
315 , mMotionAfterPasting(NoMoveScreenWindow)
318 , m_font(QStringLiteral(
"Monospace"), 12)
320 , m_full_cursor_height(false)
321 , _drawLineChars(true)
322 , m_backgroundOpacity(1.0)
323 , m_customColorScheme(new CustomColorScheme(this))
335 _topMargin = _topBaseMargin;
336 _leftMargin = _leftBaseMargin;
338 m_palette = qApp->palette();
355 _blinkTimer =
new QTimer(
this);
356 connect(_blinkTimer, SIGNAL(timeout()),
this, SLOT(blinkEvent()));
357 _blinkCursorTimer =
new QTimer(
this);
363 setBracketedPasteMode(
false);
373 _scrollBar->setVisible(
false);
395TerminalDisplay::~TerminalDisplay()
399 qApp->removeEventFilter(
this);
402 delete _outputSuspendedLabel;
460static void drawLineChar(QPainter &paint,
int x,
int y,
int w,
int h, uint8_t code)
468 quint32 toDraw = LineChars[code];
472 paint.
drawLine(cx - 1, y, cx - 1, cy - 2);
476 paint.
drawLine(cx + 1, y, cx + 1, cy - 2);
480 paint.
drawLine(cx - 1, cy + 2, cx - 1, ey);
484 paint.
drawLine(cx + 1, cy + 2, cx + 1, ey);
488 paint.
drawLine(x, cy - 1, cx - 2, cy - 1);
492 paint.
drawLine(x, cy + 1, cx - 2, cy + 1);
496 paint.
drawLine(cx + 2, cy - 1, ex, cy - 1);
500 paint.
drawLine(cx + 2, cy + 1, ex, cy + 1);
525static void drawOtherChar(QPainter &paint,
int x,
int y,
int w,
int h, uchar code)
528 const int cx = x + w / 2;
529 const int cy = y + h / 2;
530 const int ex = x + w - 1;
531 const int ey = y + h - 1;
534 if (0x4C <= code && code <= 0x4F) {
535 const int xHalfGap = qMax(w / 15, 1);
536 const int yHalfGap = qMax(h / 15, 1);
539 paint.
drawLine(x, cy - 1, cx - xHalfGap - 1, cy - 1);
540 paint.
drawLine(x, cy + 1, cx - xHalfGap - 1, cy + 1);
541 paint.
drawLine(cx + xHalfGap, cy - 1, ex, cy - 1);
542 paint.
drawLine(cx + xHalfGap, cy + 1, ex, cy + 1);
545 paint.
drawLine(x, cy, cx - xHalfGap - 1, cy);
546 paint.
drawLine(cx + xHalfGap, cy, ex, cy);
549 paint.
drawLine(cx - 1, y, cx - 1, cy - yHalfGap - 1);
550 paint.
drawLine(cx + 1, y, cx + 1, cy - yHalfGap - 1);
551 paint.
drawLine(cx - 1, cy + yHalfGap, cx - 1, ey);
552 paint.
drawLine(cx + 1, cy + yHalfGap, cx + 1, ey);
555 paint.
drawLine(cx, y, cx, cy - yHalfGap - 1);
556 paint.
drawLine(cx, cy + yHalfGap, cx, ey);
562 else if (0x6D <= code && code <= 0x70) {
563 const int r = w * 3 / 8;
569 paint.
drawArc(cx, cy, d, d, 90 * 16, 90 * 16);
574 paint.
drawArc(cx - d, cy, d, d, 0 * 16, 90 * 16);
579 paint.
drawArc(cx - d, cy - d, d, d, 270 * 16, 90 * 16);
584 paint.
drawArc(cx, cy - d, d, d, 180 * 16, 90 * 16);
590 else if (0x71 <= code && code <= 0x73) {
606void TerminalDisplay::drawLineCharString(QPainter &painter,
int x,
int y, QStringView str,
const Character *attributes)
const
608 const QPen ¤tPen = painter.
pen();
610 if ((attributes->
rendition & RE_BOLD) && _boldIntense) {
611 QPen boldPen(currentPen);
616 for (qsizetype i = 0; i < str.
size(); i++) {
617 uint8_t code =
static_cast<uint8_t
>(str[i].unicode() & 0xffU);
619 drawLineChar(painter, qRound(
x + (_fontWidth * i)),
y, qRound(_fontWidth), qRound(_fontHeight), code);
621 drawOtherChar(painter, qRound(
x + (_fontWidth * i)),
y, qRound(_fontWidth), qRound(_fontHeight), code);
624 painter.
setPen(currentPen);
629 if (_cursorShape == shape) {
633 _cursorShape = shape;
644 if (useForegroundColor)
651 _cursorColor = color;
660 if (m_backgroundOpacity != backgroundOpacity) {
661 m_backgroundOpacity = backgroundOpacity;
665 QColor color = m_scheme->backgroundColor();
669 Q_EMIT backgroundOpacityChanged();
675void TerminalDisplay::drawBackground(
QPainter &painter,
const QRect &rect,
const QColor &backgroundColor,
bool useOpacitySetting)
680 if (useOpacitySetting) {
681 QColor color(backgroundColor);
682 color.setAlphaF(_opacity);
689 painter.
fillRect(rect, backgroundColor);
692void TerminalDisplay::drawCursor(QPainter &painter,
694 const QColor &foregroundColor,
696 bool &invertCharacterColor)
698 QRect cursorRect = rect;
700 cursorRect.
setHeight(qRound(_fontHeight) - ((m_full_cursor_height) ? 0 : _lineSpacing - 1));
702 if (!_cursorBlinking) {
703 if (_cursorColor.isValid())
704 painter.
setPen(_cursorColor);
706 painter.
setPen(foregroundColor);
711 float penWidth = qMax(1, painter.
pen().
width());
715 +penWidth / 2 + fmod(penWidth, 2),
716 -penWidth / 2 - fmod(penWidth, 2),
717 -penWidth / 2 - fmod(penWidth, 2)));
721 painter.
fillRect(cursorRect, _cursorColor.isValid() ? _cursorColor : foregroundColor);
723 if (!_cursorColor.isValid()) {
726 invertCharacterColor =
true;
736void TerminalDisplay::drawCharacters(QPainter &painter,
const QRect &rect,
const QString &text,
const Character *style,
bool invertCharacterColor)
739 if (_blinking && (style->
rendition & RE_BLINK))
747 bool useBold = ((style->
rendition & RE_BOLD) && _boldIntense) || font().bold();
748 const bool useUnderline = style->
rendition & RE_UNDERLINE || font().underline();
749 const bool useItalic = style->
rendition & RE_ITALIC || font().italic();
750 const bool useStrikeOut = style->
rendition & RE_STRIKEOUT || font().strikeOut();
751 const bool useOverline = style->
rendition & RE_OVERLINE || font().overline();
755 QFont font = painter.
font();
756 if (font.bold() != useBold || font.underline() != useUnderline || font.italic() != useItalic || font.strikeOut() != useStrikeOut
757 || font.overline() != useOverline) {
758 font.setBold(useBold);
759 font.setUnderline(useUnderline);
760 font.setItalic(useItalic);
761 font.setStrikeOut(useStrikeOut);
762 font.setOverline(useOverline);
768 const QColor color = textColor.
color(_colorTable);
769 QPen pen = painter.
pen();
770 if (pen.
color() != color) {
776 if (isLineCharString(text))
777 drawLineCharString(painter, rect.
x(), rect.
y(), text, style);
786 painter.
drawText(rect.
x(), rect.
y() + _fontAscent + _lineSpacing, text);
788 painter.
drawText(rect.
x(), rect.
y() + _fontAscent + _lineSpacing, LTR_OVERRIDE_CHAR + text);
793void TerminalDisplay::drawTextFragment(QPainter &painter,
const QRect &rect,
const QString &text,
const Character *style)
802 if (backgroundColor != palette().
window().color())
803 drawBackground(painter, rect, backgroundColor,
false );
807 bool invertCharacterColor =
false;
809 drawCursor(painter, rect, foregroundColor, backgroundColor, invertCharacterColor);
812 drawCharacters(painter, rect, text, style, invertCharacterColor);
834void TerminalDisplay::scrollImage(
int lines,
const QRect &screenWindowRegion)
839 if (_outputSuspendedLabel && _outputSuspendedLabel->
isVisible())
846 QRect region = screenWindowRegion;
850 if (lines == 0 || _image.empty() || !region.
isValid() || (region.
top() + abs(lines)) >= region.
bottom() || this->_lines <= region.
height())
854 if (_resizeWidget && _resizeWidget->
isVisible())
855 _resizeWidget->
hide();
867 int scrollBarWidth = _scrollBar->
isHidden() ? 0
869 : _scrollBar->
width();
870 const int SCROLLBAR_CONTENT_GAP = scrollBarWidth == 0 ? 0 : 1;
872 if (_scrollbarLocation == QTermWidget::ScrollBarLeft) {
873 scrollRect.
setLeft(scrollBarWidth + SCROLLBAR_CONTENT_GAP);
877 scrollRect.
setRight(
width() - scrollBarWidth - SCROLLBAR_CONTENT_GAP);
879 void *firstCharPos = &_image[region.
top() * this->_columns];
880 void *lastCharPos = &_image[(region.
top() + abs(lines)) * this->_columns];
882 int top = _topMargin + (region.
top() * qRound(_fontHeight));
883 int linesToMove = region.
height() - abs(lines);
884 int bytesToMove = linesToMove * this->_columns *
sizeof(Character);
886 Q_ASSERT(linesToMove > 0);
887 Q_ASSERT(bytesToMove > 0);
892 Q_ASSERT((
char *)lastCharPos + bytesToMove < (
char *)(_image.data() + (this->_lines * this->_columns)));
894 Q_ASSERT((lines * this->_columns) < _imageSize);
897 memmove(firstCharPos, lastCharPos, bytesToMove);
903 Q_ASSERT((
char *)firstCharPos + bytesToMove < (
char *)(_image.data() + (this->_lines * this->_columns)));
906 memmove(lastCharPos, firstCharPos, bytesToMove);
909 scrollRect.
setTop(top + abs(lines) * qRound(_fontHeight));
911 scrollRect.
setHeight(linesToMove * qRound(_fontHeight));
919QRegion TerminalDisplay::hotSpotRegion()
const
922 const auto hotSpots = _filterChain->hotSpots();
925 if (hotSpot->startLine() == hotSpot->endLine()) {
926 r.
setLeft(hotSpot->startColumn());
927 r.
setTop(hotSpot->startLine());
930 region |= imageToWidget(r);
933 r.
setLeft(hotSpot->startColumn());
934 r.
setTop(hotSpot->startLine());
937 region |= imageToWidget(r);
939 for (
int line = hotSpot->startLine() + 1; line < hotSpot->endLine(); line++) {
944 region |= imageToWidget(r);
948 r.
setTop(hotSpot->endLine());
951 region |= imageToWidget(r);
963 QRegion preUpdateHotSpots = hotSpotRegion();
970 _filterChain->setImage(_screenWindow->getImage(), _screenWindow->windowLines(), _screenWindow->windowColumns(), _screenWindow->getLineProperties());
971 _filterChain->process();
973 QRegion postUpdateHotSpots = hotSpotRegion();
975 update(preUpdateHotSpots | postUpdateHotSpots);
994 if (_image.empty()) {
1000 auto newimg = _screenWindow->getImage();
1001 int lines = _screenWindow->windowLines();
1002 int columns = _screenWindow->windowColumns();
1004 setScroll(_screenWindow->currentLine(), _screenWindow->lineCount());
1006 Q_ASSERT(this->_usedLines <= this->_lines);
1007 Q_ASSERT(this->_usedColumns <= this->_columns);
1011 QPoint tL = contentsRect().topLeft();
1014 _hasBlinker =
false;
1020 const int linesToUpdate = qMin(this->_lines, qMax(0, lines));
1021 const int columnsToUpdate = qMin(this->_columns, qMax(0, columns));
1023 std::vector<QChar> disstrU(columnsToUpdate);
1024 std::vector<char> dirtyMask(columnsToUpdate + 2);
1030 int dirtyLineCount = 0;
1032 for (
y = 0;
y < linesToUpdate; ++
y) {
1033 const Character *currentLine = &_image[
y * _columns];
1034 const Character *
const newLine = &newimg[
y * columns];
1036 bool updateLine =
false;
1041 memset(dirtyMask.data(), 0, columnsToUpdate + 2);
1043 for (
x = 0;
x < columnsToUpdate; ++
x) {
1044 if (newLine[
x] != currentLine[
x]) {
1045 dirtyMask[
x] =
true;
1050 for (
x = 0;
x < columnsToUpdate; ++
x) {
1051 _hasBlinker |= (newLine[
x].
rendition & RE_BLINK);
1062 bool lineDraw = isLineChar(c);
1063 bool doubleWidth = (
x + 1 == columnsToUpdate) ?
false : (newLine[
x + 1].
character.
unicode() == 0);
1066 if (newLine[
x].foregroundColor != cf)
1068 int lln = columnsToUpdate -
x;
1069 for (len = 1; len < lln; ++len) {
1075 bool nextIsDoubleWidth = (
x + len + 1 == columnsToUpdate) ?
false : (newLine[
x + len + 1].
character.
unicode() == 0);
1078 || isLineChar(c) != lineDraw || nextIsDoubleWidth != doubleWidth)
1084 bool saveFixedFont = _fixedFont;
1092 _fixedFont = saveFixedFont;
1101 if (_lineProperties.size() >
y)
1102 updateLine |= (_lineProperties[
y] & LINE_DOUBLEHEIGHT);
1111 QRect dirtyRect =
QRect(_leftMargin + tLx, _topMargin + tLy + qRound(_fontHeight) *
y, _fontWidth * columnsToUpdate, qRound(_fontHeight));
1113 dirtyRegion |= dirtyRect;
1118 memcpy((
void *)currentLine, (
const void *)newLine, columnsToUpdate *
sizeof(
Character));
1123 if (linesToUpdate < _usedLines) {
1124 dirtyRegion |=
QRect(_leftMargin + tLx,
1125 _topMargin + tLy + qRound(_fontHeight) * linesToUpdate,
1126 _fontWidth * this->_columns,
1127 qRound(_fontHeight) * (_usedLines - linesToUpdate));
1129 _usedLines = linesToUpdate;
1131 if (columnsToUpdate < _usedColumns) {
1132 dirtyRegion |=
QRect(_leftMargin + tLx + columnsToUpdate * _fontWidth,
1134 _fontWidth * (_usedColumns - columnsToUpdate),
1135 qRound(_fontHeight) * this->_lines);
1137 _usedColumns = columnsToUpdate;
1139 dirtyRegion |= _inputMethodData.previousPreeditRect;
1142 update(dirtyRegion);
1144 if (_hasBlinker && !_blinkTimer->isActive())
1145 _blinkTimer->start(TEXT_BLINK_DELAY);
1146 if (!_hasBlinker && _blinkTimer->isActive()) {
1147 _blinkTimer->stop();
1152void TerminalDisplay::showResizeNotification()
1158 if (_hasBlinkingCursor != blink)
1159 Q_EMIT blinkingCursorStateChanged();
1161 _hasBlinkingCursor = blink;
1163 if (blink && !_blinkCursorTimer->isActive())
1166 if (!blink && _blinkCursorTimer->isActive()) {
1167 _blinkCursorTimer->stop();
1168 if (_cursorBlinking)
1171 _cursorBlinking =
false;
1177 _allowBlinkingText = blink;
1179 if (blink && !_blinkTimer->isActive())
1180 _blinkTimer->start(TEXT_BLINK_DELAY);
1182 if (!blink && _blinkTimer->isActive()) {
1183 _blinkTimer->stop();
1194 _cursorBlinking =
false;
1197 _blinkCursorTimer->
stop();
1201 _blinkTimer->
stop();
1206 if (_hasBlinkingCursor) {
1207 _blinkCursorTimer->
start();
1212 _blinkTimer->start();
1216void TerminalDisplay::paint(QPainter *painter)
1220 drawContents(*painter, dirtyRect);
1223QPoint TerminalDisplay::cursorPosition()
const
1226 return _screenWindow->cursorPosition();
1231QRect TerminalDisplay::preeditRect()
const
1233 const int preeditLength = string_width(_inputMethodData.preeditString);
1235 if (preeditLength == 0)
1238 return QRect(_leftMargin + qRound(_fontWidth) * cursorPosition().
x(),
1239 _topMargin + qRound(_fontHeight) * cursorPosition().
y(),
1240 qRound(_fontWidth) * preeditLength,
1241 qRound(_fontHeight));
1244void TerminalDisplay::drawInputMethodPreeditString(QPainter &painter,
const QRect &rect)
1246 if (_inputMethodData.preeditString.isEmpty())
1249 const QPoint cursorPos = cursorPosition();
1251 bool invertColors =
false;
1252 const QColor background = _colorTable[DEFAULT_BACK_COLOR].color;
1253 const QColor foreground = _colorTable[DEFAULT_FORE_COLOR].color;
1254 const Character *style = &_image[loc(cursorPos.
x(), cursorPos.
y())];
1256 drawBackground(painter, rect, background,
true);
1257 drawCursor(painter, rect, foreground, background, invertColors);
1258 drawCharacters(painter, rect, _inputMethodData.preeditString, style, invertColors);
1260 _inputMethodData.previousPreeditRect = rect;
1265 return _filterChain.get();
1268void TerminalDisplay::paintFilters(
QPainter &painter)
1273 int leftMargin = _leftBaseMargin
1275 ? _scrollBar->
width()
1278 auto charPos = getCharacterPosition(cursorPos);
1279 Character cursorCharacter = _image[loc(charPos.columns, charPos.lines)];
1287 for (
const auto spot : spots) {
1289 if (spot->type() == Filter::HotSpot::Link) {
1291 if (spot->startLine() == spot->endLine()) {
1292 r.
setCoords(spot->startColumn() * qRound(_fontWidth) + 1 + leftMargin,
1293 spot->startLine() * qRound(_fontHeight) + 1 + _topBaseMargin,
1294 spot->endColumn() * qRound(_fontWidth) - 1 + leftMargin,
1295 (spot->endLine() + 1) * qRound(_fontHeight) - 1 + _topBaseMargin);
1298 r.
setCoords(spot->startColumn() * qRound(_fontWidth) + 1 + leftMargin,
1299 spot->startLine() * qRound(_fontHeight) + 1 + _topBaseMargin,
1300 _columns * qRound(_fontWidth) - 1 + leftMargin,
1301 (spot->startLine() + 1) * qRound(_fontHeight) - 1 + _topBaseMargin);
1303 for (
int line = spot->startLine() + 1; line < spot->endLine(); line++) {
1304 r.
setCoords(0 * qRound(_fontWidth) + 1 + leftMargin,
1305 line * qRound(_fontHeight) + 1 + _topBaseMargin,
1306 _columns * qRound(_fontWidth) - 1 + leftMargin,
1307 (line + 1) * qRound(_fontHeight) - 1 + _topBaseMargin);
1310 r.
setCoords(0 * qRound(_fontWidth) + 1 + leftMargin,
1311 spot->endLine() * qRound(_fontHeight) + 1 + _topBaseMargin,
1312 spot->endColumn() * qRound(_fontWidth) - 1 + leftMargin,
1313 (spot->endLine() + 1) * qRound(_fontHeight) - 1 + _topBaseMargin);
1318 for (
int line = spot->startLine(); line <= spot->endLine(); line++) {
1319 int startColumn = 0;
1320 int endColumn = _columns - 1;
1325 while (QChar(_image[loc(endColumn, line)].character).isSpace() && endColumn > 0)
1332 if (line == spot->startLine())
1333 startColumn = spot->startColumn();
1334 if (line == spot->endLine())
1335 endColumn = spot->endColumn();
1347 r.
setCoords(startColumn * qRound(_fontWidth) + 1 + leftMargin,
1348 line * qRound(_fontHeight) + 1 + _topBaseMargin,
1349 endColumn * qRound(_fontWidth) - 1 + leftMargin,
1350 (line + 1) * qRound(_fontHeight) - 1 + _topBaseMargin);
1352 if (spot->type() == Filter::HotSpot::Link) {
1353 QFontMetricsF metrics(font());
1357 qreal baseline = (qreal)r.
bottom() - metrics.descent();
1359 qreal underlinePos = baseline + metrics.underlinePos();
1367 else if (spot->type() == Filter::HotSpot::Marker) {
1369 painter.
fillRect(r, QBrush(QColor(255, 0, 0, 120)));
1375int TerminalDisplay::textWidth(
const int startColumn,
const int length,
const int line)
const
1377 QFontMetricsF fm(font());
1379 for (
int column = 0; column < length; column++) {
1380 result += fm.horizontalAdvance(_image[loc(startColumn + column, line)].character);
1385QRect TerminalDisplay::calculateTextArea(
int topLeftX,
int topLeftY,
int startColumn,
int line,
int length)
1387 int left = _fixedFont ? qRound(_fontWidth) * startColumn : textWidth(0, startColumn, line);
1388 int top = qRound(_fontHeight) * line;
1389 int width = _fixedFont ? qRound(_fontWidth) * length : textWidth(startColumn, length, line);
1390 return {_leftMargin + topLeftX +
left, _topMargin + topLeftY + top,
width, qRound(_fontHeight)};
1393void TerminalDisplay::drawContents(QPainter &paint,
const QRect &rect)
1396 drawBackground(paint, contentsRect(), _colorTable[DEFAULT_BACK_COLOR].color,
true);
1398 QPoint tL = contentsRect().topLeft();
1402 int lux = qMin(_usedColumns - 1, qMax(0, qRound((rect.
left() - tLx - _leftMargin) / _fontWidth)));
1403 int luy = qMin(_usedLines - 1, qMax(0, qRound((rect.
top() - tLy - _topMargin) / _fontHeight)));
1404 int rlx = qMin(_usedColumns - 1, qMax(0, qRound((rect.
right() - tLx - _leftMargin) / _fontWidth)));
1405 int rly = qMin(_usedLines - 1, qMax(0, qRound((rect.
bottom() - tLy - _topMargin) / _fontHeight)));
1407 if (_image.empty()) {
1411 const int bufferSize = _usedColumns;
1414 for (
int y = luy;
y <= rly;
y++) {
1415 char16_t c = _image[loc(lux,
y)].character.unicode();
1419 for (;
x <= rlx;
x++) {
1424 unistr.
resize(bufferSize);
1427 if (_image[loc(
x,
y)].rendition & RE_EXTENDED_CHAR) {
1429 ushort extendedCharLength = 0;
1431 for (
int index = 0; index < extendedCharLength; index++) {
1432 Q_ASSERT(p < bufferSize);
1433 unistr[p++] = chars[index];
1437 c = _image[loc(
x,
y)].character.unicode();
1439 Q_ASSERT(p < bufferSize);
1444 bool lineDraw = isLineChar(c);
1445 bool doubleWidth = (_image[qMin(loc(
x,
y) + 1, _imageSize)].character.unicode() == 0);
1446 CharacterColor currentForeground = _image[loc(
x,
y)].foregroundColor;
1447 CharacterColor currentBackground = _image[loc(
x,
y)].backgroundColor;
1448 quint8 currentRendition = _image[loc(
x,
y)].rendition;
1450 while (
x + len <= rlx && _image[loc(
x + len,
y)].foregroundColor == currentForeground
1451 && _image[loc(
x + len,
y)].backgroundColor == currentBackground && _image[loc(
x + len,
y)].rendition == currentRendition
1452 && (_image[qMin(loc(
x + len,
y) + 1, _imageSize)].character.unicode() == 0) == doubleWidth
1453 && isLineChar(c = _image[loc(
x + len,
y)].character.unicode()) == lineDraw)
1461 if ((
x + len < _usedColumns) && (!_image[loc(
x + len,
y)].character.unicode()))
1464 bool save__fixedFont = _fixedFont;
1470 QTransform textScale;
1472 if (
y < _lineProperties.size()) {
1473 if (_lineProperties[
y] & LINE_DOUBLEWIDTH)
1474 textScale.
scale(2, 1);
1476 if (_lineProperties[
y] & LINE_DOUBLEHEIGHT)
1477 textScale.
scale(1, 2);
1481 paint.setWorldTransform(textScale,
true);
1484 QRect textArea = calculateTextArea(tLx, tLy,
x,
y, len);
1495 drawTextFragment(paint, textArea, unistr, &_image[loc(
x,
y)]);
1499 _fixedFont = save__fixedFont;
1502 paint.setWorldTransform(textScale.
inverted(),
true);
1504 if (
y < _lineProperties.size() - 1) {
1510 if (_lineProperties[
y] & LINE_DOUBLEHEIGHT)
1519void TerminalDisplay::blinkEvent()
1521 if (!_allowBlinkingText)
1524 _blinking = !_blinking;
1532QRect TerminalDisplay::imageToWidget(
const QRect &imageArea)
const
1535 result.
setLeft(_leftMargin + qRound(_fontWidth) * imageArea.
left());
1536 result.
setTop(_topMargin + qRound(_fontHeight) * imageArea.
top());
1543void TerminalDisplay::updateCursor()
1545 QRect cursorRect = imageToWidget(QRect(cursorPosition(), QSize(1, 1)));
1549void TerminalDisplay::blinkCursorEvent()
1551 _cursorBlinking = !_cursorBlinking;
1561void TerminalDisplay::resizeEvent(QResizeEvent *)
1567void TerminalDisplay::propagateSize()
1570 setSize(_columns, _lines);
1573 if (!_image.empty())
1577void TerminalDisplay::updateImageSize()
1579 auto oldimg = _image;
1580 int oldlin = _lines;
1581 int oldcol = _columns;
1586 int lines = qMin(oldlin, _lines);
1587 int columns = qMin(oldcol, _columns);
1589 if (!oldimg.empty()) {
1590 for (
int line = 0; line < lines; line++) {
1591 memcpy((
void *)&_image[_columns * line], (
void *)&oldimg[oldcol * line], columns *
sizeof(Character));
1597 _screenWindow->setWindowLines(_lines);
1599 _resizing = (oldlin != _lines) || (oldcol != _columns);
1602 showResizeNotification();
1603 Q_EMIT changedContentSizeSignal(_contentHeight, _contentWidth);
1614void TerminalDisplay::showEvent(QShowEvent *)
1616 Q_EMIT changedContentSizeSignal(_contentHeight, _contentWidth);
1618void TerminalDisplay::hideEvent(QHideEvent *)
1620 Q_EMIT changedContentSizeSignal(_contentHeight, _contentWidth);
1629void TerminalDisplay::scrollBarPositionChanged(
int)
1634 _screenWindow->scrollTo(_scrollBar->value());
1640 const bool atEndOfOutput = (_scrollBar->value() == _scrollBar->maximum());
1641 _screenWindow->setTrackOutput(atEndOfOutput);
1646 Q_EMIT scrollbarValueChanged();
1656 if (_scrollBar->minimum() == 0 && _scrollBar->maximum() == (slines - _lines) && _scrollBar->value() ==
cursor) {
1661 _scrollBar->setRange(0, slines - _lines);
1662 _scrollBar->setSingleStep(1);
1663 _scrollBar->setPageStep(_lines);
1664 _scrollBar->setValue(
cursor);
1671 _scrollBar->setValue(_scrollBar->maximum());
1674 _screenWindow->scrollTo(_scrollBar->value() + 1);
1675 _screenWindow->setTrackOutput(_screenWindow->atEndOfOutput());
1680 if (_scrollbarLocation == position)
1683 if (position == QTermWidget::NoScrollBar)
1688 _topMargin = _leftMargin = 1;
1689 _scrollbarLocation = position;
1695void TerminalDisplay::mousePressEvent(
QMouseEvent *ev)
1698 mouseTripleClickEvent(ev);
1708 auto charPos = getCharacterPosition(ev->
pos());
1710 auto [charColumn, charLine] = charPos;
1713 _lineSelectionMode =
false;
1714 _wordSelectionMode =
false;
1716 Q_EMIT isBusySelecting(
true);
1718 bool selected =
false;
1724 selected = _screenWindow->isSelected(charColumn, charLine);
1728 dragInfo.state = diPending;
1729 dragInfo.start = ev->
pos();
1732 dragInfo.state = diNone;
1738 _screenWindow->clearSelection();
1741 pos.
ry() += _scrollBar->value();
1742 _iPntSel = _pntSel = pos;
1746 Q_EMIT mouseSignal(0, charColumn + 1, charLine + 1 + _scrollBar->value() - _scrollBar->maximum(), 0);
1749 Filter::HotSpot *spot = _filterChain->hotSpotAt(charLine, charColumn);
1750 if (spot && spot->
type() == Filter::HotSpot::Link)
1751 spot->
activate(QLatin1String(
"click-action"));
1757 Q_EMIT mouseSignal(1, charColumn + 1, charLine + 1 + _scrollBar->value() - _scrollBar->maximum(), 0);
1762 Q_EMIT mouseSignal(2, charColumn + 1, charLine + 1 + _scrollBar->value() - _scrollBar->maximum(), 0);
1768 auto pos = getCharacterPosition(position);
1770 Filter::HotSpot *spot = _filterChain->hotSpotAt(pos.lines, pos.columns);
1775void TerminalDisplay::mouseMoveEvent(
QMouseEvent *ev)
1777 int leftMargin = _leftBaseMargin
1779 ? _scrollBar->
width()
1782 auto charPos = getCharacterPosition(ev->
pos());
1786 Filter::HotSpot *spot = _filterChain->hotSpotAt(charPos.lines, charPos.columns);
1787 if (spot && spot->
type() == Filter::HotSpot::Link) {
1788 QRegion previousHotspotArea = _mouseOverHotspotArea;
1789 _mouseOverHotspotArea =
QRegion();
1793 spot->
startLine() * qRound(_fontHeight) + _topBaseMargin,
1794 spot->
endColumn() * qRound(_fontWidth) + leftMargin,
1795 (spot->
endLine() + 1) * qRound(_fontHeight) - 1 + _topBaseMargin);
1796 _mouseOverHotspotArea |= r;
1799 spot->
startLine() * qRound(_fontHeight) + _topBaseMargin,
1800 _columns * qRound(_fontWidth) - 1 + leftMargin,
1801 (spot->
startLine() + 1) * qRound(_fontHeight) + _topBaseMargin);
1802 _mouseOverHotspotArea |= r;
1803 for (
int line = spot->
startLine() + 1; line < spot->endLine(); line++) {
1804 r.
setCoords(0 * qRound(_fontWidth) + leftMargin,
1805 line * qRound(_fontHeight) + _topBaseMargin,
1806 _columns * qRound(_fontWidth) + leftMargin,
1807 (line + 1) * qRound(_fontHeight) + _topBaseMargin);
1808 _mouseOverHotspotArea |= r;
1810 r.
setCoords(0 * qRound(_fontWidth) + leftMargin,
1811 spot->
endLine() * qRound(_fontHeight) + _topBaseMargin,
1812 spot->
endColumn() * qRound(_fontWidth) + leftMargin,
1813 (spot->
endLine() + 1) * qRound(_fontHeight) + _topBaseMargin);
1814 _mouseOverHotspotArea |= r;
1816 update(_mouseOverHotspotArea | previousHotspotArea);
1817 }
else if (!_mouseOverHotspotArea.isEmpty()) {
1818 update(_mouseOverHotspotArea);
1820 _mouseOverHotspotArea = QRegion();
1839 Q_EMIT mouseSignal(button, charPos.columns + 1, charPos.lines + 1 + _scrollBar->value() - _scrollBar->maximum(), 1);
1844 if (dragInfo.state == diPending) {
1851#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1852 if (ev->
position().
x() > dragInfo.start.x() + distance || ev->
position().
x() < dragInfo.start.x() - distance
1853 || ev->
position().
y() > dragInfo.start.y() + distance || ev->
position().
y() < dragInfo.start.y() - distance)
1855 if (ev->
pos().
x() > dragInfo.start.x() + distance || ev->
pos().
x() < dragInfo.start.x() - distance
1856 || ev->
pos().
y() > dragInfo.start.y() + distance || ev->
pos().
y() < dragInfo.start.y() - distance)
1860 Q_EMIT isBusySelecting(
false);
1862 _screenWindow->clearSelection();
1866 }
else if (dragInfo.state == diDragging) {
1879 extendSelection(ev->
pos());
1882void TerminalDisplay::extendSelection(
const QPoint &position)
1884 QPoint pos = position;
1890 QPoint tL = contentsRect().topLeft();
1893 int scroll = _scrollBar->value();
1899 int linesBeyondWidget = 0;
1901 QRect textBounds(tLx + _leftMargin, tLy + _topMargin, _usedColumns * qRound(_fontWidth) - 1, _usedLines * qRound(_fontHeight) - 1);
1904 QPoint oldpos = pos;
1906 pos.
setX(qBound(textBounds.left(), pos.
x(), textBounds.right()));
1907 pos.
setY(qBound(textBounds.top(), pos.
y(), textBounds.bottom()));
1909 if (oldpos.
y() > textBounds.bottom()) {
1910 linesBeyondWidget = (oldpos.
y() - textBounds.bottom()) / qRound(_fontHeight);
1911 _scrollBar->setValue(_scrollBar->value() + linesBeyondWidget + 1);
1913 if (oldpos.
y() < textBounds.top()) {
1914 linesBeyondWidget = (textBounds.top() - oldpos.
y()) / qRound(_fontHeight);
1915 _scrollBar->setValue(_scrollBar->value() - linesBeyondWidget - 1);
1919 getCharacterPosition(pos);
1921 QPoint _iPntSelCorr = _iPntSel;
1922 _iPntSelCorr.
ry() -= _scrollBar->value();
1923 QPoint _pntSelCorr = _pntSel;
1924 _pntSelCorr.
ry() -= _scrollBar->value();
1925 bool swapping =
false;
1927 if (_wordSelectionMode) {
1932 bool left_not_right = (here.
y() < _iPntSelCorr.
y() || (here.
y() == _iPntSelCorr.
y() && here.
x() < _iPntSelCorr.
x()));
1933 bool old_left_not_right = (_pntSelCorr.
y() < _iPntSelCorr.
y() || (_pntSelCorr.
y() == _iPntSelCorr.
y() && _pntSelCorr.
x() < _iPntSelCorr.
x()));
1934 swapping = left_not_right != old_left_not_right;
1937 QPoint
left = left_not_right ? here : _iPntSelCorr;
1939 if (i >= 0 && i <= _imageSize) {
1940 selClass = charClass(_image[i].character);
1941 while (((
left.x() > 0) || (
left.y() > 0 && (_lineProperties[
left.y() - 1] & LINE_WRAPPED))) && charClass(_image[i - 1].character) == selClass) {
1946 left.rx() = _usedColumns - 1;
1953 QPoint
right = left_not_right ? _iPntSelCorr : here;
1955 if (i >= 0 && i <= _imageSize) {
1956 selClass = charClass(_image[i].character);
1957 while (((
right.x() < _usedColumns - 1) || (
right.y() < _usedLines - 1 && (_lineProperties[
right.y()] & LINE_WRAPPED)))
1958 && charClass(_image[i + 1].character) == selClass) {
1960 if (
right.x() < _usedColumns - 1)
1970 if (left_not_right) {
1980 if (_lineSelectionMode) {
1982 bool above_not_below = (here.
y() < _iPntSelCorr.
y());
1984 QPoint above = above_not_below ? here : _iPntSelCorr;
1985 QPoint below = above_not_below ? _iPntSelCorr : here;
1987 while (above.
y() > 0 && (_lineProperties[above.
y() - 1] & LINE_WRAPPED))
1989 while (below.
y() < _usedLines - 1 && (_lineProperties[below.
y()] & LINE_WRAPPED))
1993 below.
setX(_usedColumns - 1);
1996 if (above_not_below) {
2004 QPoint newSelBegin = QPoint(ohere.
x(), ohere.
y());
2005 swapping = !(_tripleSelBegin == newSelBegin);
2006 _tripleSelBegin = newSelBegin;
2012 if (!_wordSelectionMode && !_lineSelectionMode) {
2016 bool left_not_right = (here.
y() < _iPntSelCorr.
y() || (here.
y() == _iPntSelCorr.
y() && here.
x() < _iPntSelCorr.
x()));
2017 bool old_left_not_right = (_pntSelCorr.
y() < _iPntSelCorr.
y() || (_pntSelCorr.
y() == _iPntSelCorr.
y() && _pntSelCorr.
x() < _iPntSelCorr.
x()));
2018 swapping = left_not_right != old_left_not_right;
2021 QPoint
left = left_not_right ? here : _iPntSelCorr;
2024 QPoint
right = left_not_right ? _iPntSelCorr : here;
2025 if (
right.x() > 0 && !_columnSelectionMode) {
2027 if (i >= 0 && i <= _imageSize) {
2028 selClass = charClass(_image[i - 1].character);
2043 if (left_not_right) {
2054 if ((here == _pntSelCorr) && (scroll == _scrollBar->value()))
2060 if (_actSel < 2 || swapping) {
2061 if (_columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode) {
2062 _screenWindow->setSelectionStart(ohere.
x(), ohere.
y(),
true);
2064 _screenWindow->setSelectionStart(ohere.
x() - 1 - offset, ohere.
y(),
false);
2070 _pntSel.
ry() += _scrollBar->value();
2072 if (_columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode) {
2073 _screenWindow->setSelectionEnd(here.
x(), here.
y());
2075 _screenWindow->setSelectionEnd(here.
x() + offset, here.
y());
2079void TerminalDisplay::mouseReleaseEvent(QMouseEvent *ev)
2084 auto [charColumn, charLine] = getCharacterPosition(ev->
pos());
2087 Q_EMIT isBusySelecting(
false);
2088 if (dragInfo.state == diPending) {
2090 _screenWindow->clearSelection();
2094 setSelection(_screenWindow->selectedText(_preserveLineBreaks));
2104 Q_EMIT mouseSignal(0, charColumn + 1, charLine + 1 + _scrollBar->value() - _scrollBar->maximum(), 2);
2106 dragInfo.state = diNone;
2114CharPos TerminalDisplay::getCharacterPosition(
const QPointF &widgetPoint)
const
2116 int line, column = 0;
2118 line = (widgetPoint.
y() - contentsRect().top() - _topMargin) / qRound(_fontHeight);
2121 if (line >= _usedLines)
2122 line = _usedLines - 1;
2124 int x = widgetPoint.
x() + qRound(_fontWidth) / 2 - contentsRect().left() - _leftMargin;
2126 column =
x / qRound(_fontWidth);
2129 while (column + 1 < _usedColumns &&
x > textWidth(0, column + 1, line))
2141 if (column > _usedColumns)
2142 column = _usedColumns;
2144 return {column, line};
2160 _lineProperties = _screenWindow->getLineProperties();
2163void TerminalDisplay::mouseDoubleClickEvent(
QMouseEvent *ev)
2170 QPoint pos = getCharacterPosition(ev->
pos());
2181 _screenWindow->clearSelection();
2184 int i = loc(bgnSel.
x(), bgnSel.
y());
2186 _iPntSel.
ry() += _scrollBar->
value();
2188 _wordSelectionMode =
true;
2191 QChar selClass = charClass(_image[i].character);
2195 while (((
x > 0) || (bgnSel.
y() > 0 && (_lineProperties[bgnSel.
y() - 1] & LINE_WRAPPED))) && charClass(_image[i - 1].character) == selClass) {
2200 x = _usedColumns - 1;
2206 _screenWindow->setSelectionStart(bgnSel.
x(), bgnSel.
y(),
false);
2209 i = loc(endSel.
x(), endSel.
y());
2211 while (((
x < _usedColumns - 1) || (endSel.
y() < _usedLines - 1 && (_lineProperties[endSel.
y()] & LINE_WRAPPED)))
2212 && charClass(_image[i + 1].character) == selClass) {
2214 if (
x < _usedColumns - 1)
2225 if ((QChar(_image[i].character) == QLatin1Char(
'@')) && ((endSel.
x() - bgnSel.
x()) > 0))
2230 _screenWindow->setSelectionEnd(endSel.
x(), endSel.
y());
2232 setSelection(_screenWindow->selectedText(_preserveLineBreaks));
2235 _possibleTripleClick =
true;
2240void TerminalDisplay::wheelEvent(QWheelEvent *ev)
2250 bool canScroll = _scrollBar->maximum() > 0;
2252 _scrollBar->event(ev);
2264 int linesToScroll = abs(wheelDegrees) / 5;
2268 for (
int i = 0; i < linesToScroll; i++)
2273 auto pos = getCharacterPosition(ev->
position());
2279void TerminalDisplay::tripleClickTimeout()
2281 _possibleTripleClick =
false;
2284void TerminalDisplay::mouseTripleClickEvent(QMouseEvent *ev)
2289 _iPntSel = getCharacterPosition(ev->
pos());
2291 _screenWindow->clearSelection();
2293 _lineSelectionMode =
true;
2294 _wordSelectionMode =
false;
2297 Q_EMIT isBusySelecting(
true);
2299 while (_iPntSel.y() > 0 && (_lineProperties[_iPntSel.y() - 1] & LINE_WRAPPED))
2304 int i = loc(_iPntSel.x(), _iPntSel.y());
2305 QChar selClass = charClass(_image[i].character);
2306 int x = _iPntSel.x();
2308 while (((
x > 0) || (_iPntSel.y() > 0 && (_lineProperties[_iPntSel.y() - 1] & LINE_WRAPPED))) && charClass(_image[i - 1].character) == selClass) {
2318 _screenWindow->setSelectionStart(
x, _iPntSel.y(),
false);
2319 _tripleSelBegin = QPoint(
x, _iPntSel.y());
2321 _screenWindow->setSelectionStart(0, _iPntSel.y(),
false);
2322 _tripleSelBegin = QPoint(0, _iPntSel.y());
2325 while (_iPntSel.y() < _lines - 1 && (_lineProperties[_iPntSel.y()] & LINE_WRAPPED))
2328 _screenWindow->setSelectionEnd(_columns - 1, _iPntSel.y());
2330 setSelection(_screenWindow->selectedText(_preserveLineBreaks));
2332 _iPntSel.ry() += _scrollBar->value();
2335bool TerminalDisplay::focusNextPrevChild(
bool next)
2344QChar TerminalDisplay::charClass(QChar qch)
const
2347 return QLatin1Char(
' ');
2350 return QLatin1Char(
'a');
2357 _wordCharacters = wc;
2362 if (_mouseMarks != on) {
2365 Q_EMIT usesMouseChanged();
2373void TerminalDisplay::setBracketedPasteMode(
bool on)
2375 _bracketedPasteMode = on;
2377bool TerminalDisplay::bracketedPasteMode()
const
2379 return _bracketedPasteMode;
2390void TerminalDisplay::emitSelection(
bool useXselection,
bool appendReturn)
2401 text.
replace(QLatin1String(
"\r\n"), QLatin1String(
"\n"));
2402 text.
replace(QLatin1Char(
'\n'), QLatin1Char(
'\r'));
2404 if (_trimPastedTrailingNewlines) {
2405 text.
replace(QRegularExpression(QStringLiteral(
"\\r+$")), QString());
2437 text.
append(QLatin1Char(
'\r'));
2443 _screenWindow->clearSelection();
2445 switch (mMotionAfterPasting) {
2446 case MoveStartScreenWindow:
2451 _screenWindow->setTrackOutput(
false);
2452 _screenWindow->scrollTo(0);
2454 case MoveEndScreenWindow:
2457 case NoMoveScreenWindow:
2465 if (bracketedPasteMode() && !_disabledBracketedPasteMode) {
2471void TerminalDisplay::setSelection(
const QString &t)
2477 Q_EMIT isTextSelectedChanged();
2485 QString text = _screenWindow->selectedText(_preserveLineBreaks);
2492 emitSelection(
false,
false);
2497 emitSelection(
true,
false);
2500void TerminalDisplay::setConfirmMultilinePaste(
bool confirmMultilinePaste)
2502 _confirmMultilinePaste = confirmMultilinePaste;
2505void TerminalDisplay::setTrimPastedTrailingNewlines(
bool trimPastedTrailingNewlines)
2507 _trimPastedTrailingNewlines = trimPastedTrailingNewlines;
2518 _flowControlWarningEnabled = enable;
2526void TerminalDisplay::setMotionAfterPasting(MotionAfterPasting action)
2528 mMotionAfterPasting = action;
2531int TerminalDisplay::motionAfterPasting()
2533 return mMotionAfterPasting;
2536void TerminalDisplay::keyPressEvent(QKeyEvent *event)
2541 bool emitKeyPressSignal =
true;
2570 _screenWindow->scrollTo(0);
2577 _screenWindow->setTrackOutput( _screenWindow->atEndOfOutput() );
2583 emitKeyPressSignal =
false;
2590 if (_hasBlinkingCursor) {
2592 if (_cursorBlinking)
2595 _cursorBlinking =
false;
2598 if ( emitKeyPressSignal )
2606 switch(mMotionAfterPasting)
2608 case MoveStartScreenWindow:
2609 _screenWindow->scrollTo(0);
2611 case MoveEndScreenWindow:
2614 case NoMoveScreenWindow:
2627void TerminalDisplay::inputMethodEvent(QInputMethodEvent *event)
2632 _inputMethodData.preeditString =
event->preeditString();
2633 update(preeditRect() | _inputMethodData.previousPreeditRect);
2638void TerminalDisplay::inputMethodQuery(QInputMethodQueryEvent *event)
2647 const QPoint cursorPos = _screenWindow ? _screenWindow->cursorPosition() : QPoint(0, 0);
2650 return imageToWidget(QRect(cursorPos.
x(), cursorPos.
y(), 1, 1));
2657 return cursorPos.
x();
2662 QTextStream stream(&lineText);
2663 PlainTextDecoder decoder;
2664 decoder.
begin(&stream);
2665 decoder.
decodeLine(std::span(&_image[loc(0, cursorPos.
y())], _usedColumns), _lineProperties[cursorPos.
y()]);
2679bool TerminalDisplay::handleShortcutOverrideEvent(QKeyEvent *keyEvent)
2681 int modifiers =
keyEvent->modifiers();
2687 int modifierCount = 0;
2691 if (modifiers & currentModifier)
2693 currentModifier <<= 1;
2695 if (modifierCount < 2) {
2696 bool override =
false;
2707 int keyCode =
keyEvent->key() | modifiers;
2724bool TerminalDisplay::event(QEvent *event)
2726 bool eventHandled =
false;
2727 switch (event->type()) {
2729 eventHandled = handleShortcutOverrideEvent((QKeyEvent *)event);
2736 inputMethodQuery(
static_cast<QInputMethodQueryEvent *
>(event));
2737 eventHandled =
true;
2750void TerminalDisplay::enableBell()
2770 Q_EMIT notifyBell(message);
2778void TerminalDisplay::selectionChanged()
2780 Q_EMIT copyAvailable(_screenWindow->selectedText(
false).isEmpty() ==
false);
2783void TerminalDisplay::swapColorTable()
2786 _colorTable[1] = _colorTable[0];
2787 _colorTable[0] = color;
2788 _colorsInverted = !_colorsInverted;
2792void TerminalDisplay::clearImage()
2795 for (
int i = 0; i <= _imageSize; i++) {
2796 _image[i].character = u
' ';
2797 _image[i].foregroundColor = CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR);
2798 _image[i].backgroundColor = CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR);
2799 _image[i].rendition = DEFAULT_RENDITION;
2803void TerminalDisplay::calcGeometry()
2805 _scrollBar->resize(_scrollBar->sizeHint().width(), contentsRect().
height());
2807 switch (_scrollbarLocation) {
2808 case QTermWidget::NoScrollBar:
2809 _leftMargin = _leftBaseMargin;
2810 _contentWidth = contentsRect().width() - 2 * _leftBaseMargin;
2812 case QTermWidget::ScrollBarLeft:
2813 _leftMargin = _leftBaseMargin + scrollBarWidth;
2814 _contentWidth = contentsRect().width() - 2 * _leftBaseMargin - scrollBarWidth;
2815 _scrollBar->move(contentsRect().topLeft());
2817 case QTermWidget::ScrollBarRight:
2818 _leftMargin = _leftBaseMargin;
2819 _contentWidth = contentsRect().width() - 2 * _leftBaseMargin - scrollBarWidth;
2820 _scrollBar->move(contentsRect().topRight() - QPoint(_scrollBar->width() - 1, 0));
2824 _topMargin = _topBaseMargin;
2825 _contentHeight = contentsRect().height() - 2 * _topBaseMargin + 1;
2827 if (!_isFixedSize) {
2829 _columns = qMax(1, qRound(_contentWidth / _fontWidth));
2830 _usedColumns = qMin(_usedColumns, _columns);
2833 _lines = qMax(1, _contentHeight / qRound(_fontHeight));
2834 _usedLines = qMin(_usedLines, _lines);
2838void TerminalDisplay::makeImage()
2844 Q_ASSERT(_lines > 0 && _columns > 0);
2845 Q_ASSERT(_usedLines <= _lines && _usedColumns <= _columns);
2847 _imageSize = _lines * _columns;
2851 _image.resize(_imageSize + 1);
2857void TerminalDisplay::setSize(
int columns,
int lines)
2859 int scrollBarWidth =
2860 (_scrollBar->isHidden() || _scrollBar->style()->styleHint(
QStyle::SH_ScrollBar_Transient,
nullptr, _scrollBar)) ? 0 : _scrollBar->sizeHint().width();
2861 int horizontalMargin = 2 * _leftBaseMargin;
2862 int verticalMargin = 2 * _topBaseMargin;
2864 QSize newSize = QSize(horizontalMargin + scrollBarWidth + (columns * _fontWidth), verticalMargin + (lines * qRound(_fontHeight)));
2866 if (newSize != size()) {
2873void TerminalDisplay::setFixedSize(
int cols,
int lins)
2875 _isFixedSize =
true;
2878 _columns = qMax(1, cols);
2879 _lines = qMax(1, lins);
2880 _usedColumns = qMin(_usedColumns, _columns);
2881 _usedLines = qMin(_usedLines, _lines);
2883 if (!_image.empty()) {
2887 setSize(cols, lins);
2891QSize TerminalDisplay::sizeHint()
const
2902void TerminalDisplay::dragEnterEvent(QDragEnterEvent *event)
2904 if (event->mimeData()->hasFormat(QLatin1String(
"text/plain")))
2905 event->acceptProposedAction();
2906 if (event->mimeData()->urls().size())
2907 event->acceptProposedAction();
2910void TerminalDisplay::dropEvent(QDropEvent *event)
2913 QList<QUrl> urls =
event->mimeData()->urls();
2918 qDebug() <<
"TerminalDisplay: handling urls. It can be broken. Report any errors, please";
2919 for (
int i = 0; i < urls.
size(); i++) {
2926 urlText = url.
path();
2934 dropText += urlText;
2936 if (i != urls.
size() - 1)
2937 dropText += QLatin1Char(
' ');
2940 dropText =
event->mimeData()->text();
2946void TerminalDisplay::doDrag()
2948 dragInfo.state = diDragging;
2949 dragInfo.dragObject =
new QDrag(
this);
2950 QMimeData *mimeData =
new QMimeData;
2952 dragInfo.dragObject->setMimeData(mimeData);
2959 _outputSuspendedLabel->setVisible(suspended);
2962uint TerminalDisplay::lineSpacing()
const
2964 return _lineSpacing;
2967void TerminalDisplay::setLineSpacing(uint i)
2969 if (i != _lineSpacing) {
2972 Q_EMIT lineSpacingChanged();
2976int TerminalDisplay::margin()
const
2978 return _topBaseMargin;
2981void TerminalDisplay::setMargin(
int i)
2984 _leftBaseMargin = i;
2989#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2990void TerminalDisplay::geometryChanged(
const QRectF &newGeometry,
const QRectF &oldGeometry)
2992void TerminalDisplay::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
2995 if (newGeometry != oldGeometry) {
2996 resizeEvent(
nullptr);
3000#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
3001 QQuickPaintedItem::geometryChanged(newGeometry, oldGeometry);
3007void TerminalDisplay::update(
const QRegion ®ion)
3020void TerminalDisplay::update()
3025QRect TerminalDisplay::contentsRect()
const
3027 return QRect(0, 0, this->
width(), this->
height());
3030QSize TerminalDisplay::size()
const
3035void TerminalDisplay::setSession(KSession *session)
3044 connect(
this, &TerminalDisplay::copyAvailable, m_session, &KSession::selectionChanged);
3049 m_session->addView(
this);
3057KSession *TerminalDisplay::getSession()
3062QStringList TerminalDisplay::availableColorSchemes()
3066 std::transform(colorSchemes.begin(), colorSchemes.end(), std::back_inserter(ret), [](
auto cs) {
3072void TerminalDisplay::setColorScheme(
const QString &name)
3074 if (name != _colorScheme)
3078 disconnect(m_scheme,
nullptr,
this,
nullptr);
3081 m_scheme = [&,
this]()
3083 if(name ==
"Adaptive")
3085 return m_customColorScheme->getScheme();
3089 if (!availableColorSchemes().
contains(name))
3098 qDebug() <<
"Cannot load color scheme: " <<
name;
3102 connect(m_scheme, SIGNAL(colorChanged(
int)),
this, SLOT(applyColorScheme()));
3105 _colorScheme =
name;
3106 Q_EMIT colorSchemeChanged();
3110void TerminalDisplay::applyColorScheme()
3112 qDebug() <<
"Colors CHANGED";
3115 qDebug() <<
"Cannot apply color scheme";
3121 QColor backgroundColor = m_scheme->backgroundColor();
3122 backgroundColor.setAlphaF(m_backgroundOpacity);
3127QString TerminalDisplay::colorScheme()
const
3129 return _colorScheme;
3132void TerminalDisplay::simulateKeyPress(
int key,
int modifiers,
bool pressed, quint32 nativeScanCode,
const QString &text)
3134 Q_UNUSED(nativeScanCode);
3140void TerminalDisplay::simulateKeySequence(
const QKeySequence &keySequence)
3143#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
3150 QKeyEvent eventPress = QKeyEvent(
QEvent::KeyPress, key, modifiers, QString());
3155void TerminalDisplay::simulateWheel(
int x,
int y,
int buttons,
int modifiers, QPoint angleDelta)
3158 QWheelEvent event(QPointF(
x,
y),
3169void TerminalDisplay::simulateMouseMove(
int x,
int y,
int button,
int buttons,
int modifiers)
3172 mouseMoveEvent(&event);
3175void TerminalDisplay::simulateMousePress(
int x,
int y,
int button,
int buttons,
int modifiers)
3178 mousePressEvent(&event);
3181void TerminalDisplay::simulateMouseRelease(
int x,
int y,
int button,
int buttons,
int modifiers)
3184 mouseReleaseEvent(&event);
3187void TerminalDisplay::simulateMouseDoubleClick(
int x,
int y,
int button,
int buttons,
int modifiers)
3190 mouseDoubleClickEvent(&event);
3193QSize TerminalDisplay::getTerminalSize()
3195 return QSize(_lines, _columns);
3198bool TerminalDisplay::getUsesMouse()
3203int TerminalDisplay::getScrollbarValue()
3205 return _scrollBar->value();
3208void TerminalDisplay::setScrollbarValue(
int value)
3210 if (value != _scrollBar->value())
3212 _scrollBar->setValue(value);
3216int TerminalDisplay::getScrollbarMaximum()
3218 return _scrollBar->maximum();
3221int TerminalDisplay::getScrollbarMinimum()
3223 return _scrollBar->minimum();
3226QSize TerminalDisplay::getFontMetrics()
3228 return QSize(qRound(_fontWidth), qRound(_fontHeight));
3231void TerminalDisplay::setFullCursorHeight(
bool val)
3233 if (m_full_cursor_height != val) {
3234 m_full_cursor_height = val;
3235 Q_EMIT fullCursorHeightChanged();
3239bool TerminalDisplay::fullCursorHeight()
const
3241 return m_full_cursor_height;
3244void TerminalDisplay::itemChange(ItemChange change,
const ItemChangeData &value)
3248 if (value.boolValue && _screenWindow) {
3249 if (this->columns() != _screenWindow->columnCount() || this->lines() != _screenWindow->lineCount()) {
3250 Q_EMIT changedContentSizeSignal(_contentHeight, _contentWidth);
3261qreal TerminalDisplay::backgroundOpacity()
const
3263 return m_backgroundOpacity;
3268 return m_customColorScheme;
3276void TerminalDisplay::setReadOnly(
bool newReadOnly)
3278 if (m_readOnly == newReadOnly)
3280 m_readOnly = newReadOnly;
3281 Q_EMIT readOnlyChanged();
3284bool TerminalDisplay::isTextSelected()
const
3289 return !_screenWindow->selectedText(
false).isEmpty();
3292QString TerminalDisplay::selectedText()
const
3297 return _screenWindow->selectedText(_preserveLineBreaks);
3300void TerminalDisplay::findNext(
const QString& regexp)
3313void TerminalDisplay::findPrevious(
const QString& regexp)
3317void TerminalDisplay::matchFound(
int startColumn,
int startLine,
int endColumn,
int endLine)
3320 qDebug() <<
"Scroll to" << startLine;
3328void TerminalDisplay::noMatchFound()
void termLostFocus()
termLostFocus
void termGetFocus()
termGetFocus
void termKeyPressed(QKeyEvent *, bool)
termKeyPressed
Describes the color of a single character in the terminal.
constexpr QColor color(std::span< const ColorEntry > palette) const
Returns the color within the specified color palette.
A single character in the terminal which consists of a unicode character value, foreground and backgr...
QChar character
The unicode character value for this character.
CharacterColor foregroundColor
The foreground color used to draw this character.
CharacterColor backgroundColor
The color used to draw this character's background.
quint8 rendition
A combination of RENDITION flags which specify options for drawing the character.
An entry in a terminal display's color palette.
QList< ColorScheme * > allColorSchemes()
Returns a list of the all the available color schemes.
static ColorSchemeManager * instance()
Returns the global color scheme manager instance.
const ColorScheme * defaultColorScheme() const
Returns the default color scheme for Konsole.
const ColorScheme * findColorScheme(const QString &name)
Returns the color scheme with the given name or 0 if no scheme with that name exists.
Base class for terminal emulation back-ends.
KeyboardCursorShape
This enum describes the available shapes for the keyboard cursor.
@ UnderlineCursor
A single flat line which occupies the space at the bottom of the cursor character's area.
@ BlockCursor
A rectangular block which covers the entire area of the cursor character.
@ IBeamCursor
An cursor shaped like the capital letter 'I', similar to the IBeam cursor used in Qt/KDE text editors...
static ExtendedCharTable instance
The global ExtendedCharTable instance.
std::span< const ushort > lookupExtendedChar(ushort hash, ushort &length) const
Looks up and returns a pointer to a sequence of unicode characters which was added to the table using...
A chain which allows a group of filters to be processed as one.
Represents an area of text which matched the pattern a particular filter has been looking for.
virtual void activate(const QString &action=QString())=0
Causes the an action associated with a hotspot to be triggered.
int endLine() const
Returns the line where the hotspot area ends.
int startLine() const
Returns the line when the hotspot area starts.
int endColumn() const
Returns the column on endLine() where the hotspot area ends.
virtual QList< QAction * > actions()
Returns a list of actions associated with the hotspot which can be used in a menu or toolbar.
int startColumn() const
Returns the column on startLine() where the hotspot area starts.
Type type() const
Returns the type of the hotspot.
void decodeLine(std::span< const Character > characters, LineProperty properties) override
Converts a line of terminal characters with associated properties into a text string and writes the s...
void end() override
End decoding.
void begin(QTextStream *output) override
Begin decoding characters.
Provides a window onto a section of a terminal screen.
void setSelectionStart(int column, int line, bool columnMode)
Sets the start of the selection to the given line and column within the window.
void setSelectionEnd(int column, int line)
Sets the end of the selection to the given line and column within the window.
@ ScrollLines
Scroll the window down by a given number of lines.
@ ScrollPages
Scroll the window down by a given number of pages, where one page is windowLines() lines.
void setTrackOutput(bool trackOutput)
Specifies whether the window should automatically move to the bottom of the screen when new output is...
int currentLine() const
Returns the index of the line which is currently at the top of this window.
void clearSelection()
Clears the current selection.
void selectionChanged()
Emitted when the selection is changed.
void notifyOutputChanged()
Notifies the window that the contents of the associated terminal screen have changed.
void scrollTo(int line)
Scrolls the window so that line is at the top of the window.
void outputChanged()
Emitted when the contents of the associated terminal screen (see screen()) changes.
void setKeyboardCursorColor(bool useForegroundColor, const QColor &color)
Sets the color used to draw the keyboard cursor.
CustomColorScheme * customColorScheme
Access to the CustomColorScheme object, which allows to modify manually the colors.
void setUsesMouse(bool usesMouse)
Sets whether the program whoose output is being displayed in the view is interested in mouse events.
bool usesMouse() const
See setUsesMouse()
bool readOnly
A read only mode prevents the user from sending keyevents.
void updateFilters()
Essentially calles processFilters().
void bell(const QString &message)
Shows a notification that a bell event has occurred in the terminal.
void bracketText(QString &text) const
change and wrap text corresponding to paste mode
void outputSuspended(bool suspended)
Causes the widget to display or hide a message informing the user that terminal output has been suspe...
void pasteSelection()
Pastes the content of the selection into the display.
void setBlinkingCursor(bool blink)
Specifies whether or not the cursor blinks.
KSession * session
Register the session to handle.
void setBlinkingTextEnabled(bool blink)
Specifies whether or not text can blink.
void keyPressedSignal(QKeyEvent *e, bool fromPaste)
Emitted when the user presses a key whilst the terminal widget has focus.
void pasteClipboard()
Pastes the content of the clipboard into the display.
void updateLineProperties()
Causes the terminal display to fetch the latest line status flags from the associated terminal screen...
void setWordCharacters(const QString &wc)
Sets which characters, in addition to letters and numbers, are regarded as being part of a word for t...
void setRandomSeed(uint seed)
Sets the seed used to generate random colors for the display (in color schemes that support them).
ScreenWindow * screenWindow() const
Returns the terminal screen section which is displayed in this widget.
void setBellMode(int mode)
Sets the type of effect used to alert the user when a 'bell' occurs in the terminal session.
QColor keyboardCursorColor() const
Returns the color of the keyboard cursor, or an invalid color if the keyboard cursor color is set to ...
TerminalDisplay(QQuickItem *parent=nullptr)
Constructs a new terminal display widget with the specified parent.
void configureRequest(const QPoint &position)
Emitted when the user right clicks on the display, or right-clicks with the Shift key held down if us...
void setForegroundColor(const QColor &color)
Sets the text of the display to the specified color.
void setScrollBarPosition(QTermWidget::ScrollBarPosition position)
Specifies whether the terminal display has a vertical scroll bar, and if so whether it is shown on th...
void setBackgroundColor(const QColor &color)
Sets the background of the display to the specified color.
void setFlowControlWarningEnabled(bool enabled)
Changes whether the flow control warning box should be shown when the flow control stop key (Ctrl+S) ...
void processFilters()
Updates the filters in the display's filter chain.
void scrollToEnd()
Scroll to the bottom of the terminal (reset scrolling).
void setKeyboardCursorShape(Emulation::KeyboardCursorShape shape)
Sets the shape of the keyboard cursor.
@ NotifyBell
KDE notification.
@ SystemBeepBell
A system beep.
@ VisualBell
A silent, visual bell (eg.
@ NoScrollBar
Do not show the scroll bar.
void setBackgroundOpacity(qreal backgroundOpacity)
Sets the backgroundOpacity of the terminal display.
QList< QAction * > filterActions(const QPoint &position)
Returns a list of menu actions created by the filters for the content at the given position.
void updateImage()
Causes the terminal display to fetch the latest character image from the associated terminal screen (...
FilterChain * filterChain() const
Returns the display's filter chain.
void mouseSignal(int button, int column, int line, int eventType)
A mouse event occurred.
void overrideShortcutCheck(QKeyEvent *keyEvent, bool &override)
When a shortcut which is also a valid terminal key sequence is pressed while the terminal widget has ...
@ SelectWholeLine
Select the whole line underneath the cursor.
@ SelectForwardsFromCursor
Select from the current cursor position to the end of the line.
void setColorTable(std::array< ColorEntry, TABLE_COLORS > &&table)
Sets the terminal color palette used by the display.
void setVTFont(const QFont &font)
Sets the font used to draw the display.
void copyClipboard()
Copies the selected text to the clipboard.
void setBoldIntense(bool value)
Specifies whether characters with intense colors should be rendered as bold.
std::span< const ColorEntry > colorTable() const
Returns the terminal color palette used by the display.
Emulation::KeyboardCursorShape keyboardCursorShape() const
Returns the shape of the keyboard cursor.
uint randomSeed() const
Returns the seed used to generate random colors for the display (in color schemes that support them).
void setScroll(int cursor, int lines)
Sets the current position and range of the display's scroll bar.
void setScreenWindow(ScreenWindow *window)
Sets the terminal screen section which is displayed in this widget.
A filter chain which processes character images from terminal displays.
Type type(const QSqlDatabase &db)
void update(Part *part, const QByteArray &data, qint64 dataSize)
QString name(StandardAction id)
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
void valueChanged(int value)
QPalette palette(const QWidget *widget)
const char * constData() const const
bool isLetterOrNumber(char32_t ucs4)
bool isSpace(char32_t ucs4)
void setText(const QString &text, Mode mode)
QString text(Mode mode) const const
void setAlphaF(float alpha)
void setStyleStrategy(StyleStrategy s)
bool isEmpty() const const
qsizetype size() const const
void setText(const QString &text)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QRectF clipBoundingRect() const const
void drawArc(const QRect &rectangle, int startAngle, int spanAngle)
void drawLine(const QLine &line)
void drawPoint(const QPoint &position)
void drawRect(const QRect &rectangle)
void drawText(const QPoint &position, const QString &text)
void fillRect(const QRect &rectangle, QGradient::Preset preset)
const QFont & font() const const
const QPen & pen() const const
void setCompositionMode(CompositionMode mode)
void setFont(const QFont &font)
void setLayoutDirection(Qt::LayoutDirection direction)
void setPen(Qt::PenStyle style)
void setColor(ColorGroup group, ColorRole role, const QColor &color)
QColor color() const const
void setColor(const QColor &color)
QPoint toPoint() const const
QQuickItem(QQuickItem *parent)
bool hasActiveFocus() const const
virtual QRectF clipRect() const const
virtual bool contains(const QPointF &point) const const
QCursor cursor() const const
virtual bool event(QEvent *ev) override
virtual void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
QPointF mapFromScene(const QPointF &point) const const
QPointF mapToGlobal(const QPointF &point) const const
void setCursor(const QCursor &cursor)
void setFlags(Flags flags)
QQuickWindow * window() const const
QQuickPaintedItem(QQuickItem *parent)
void setFillColor(const QColor &)
virtual void itemChange(ItemChange change, const ItemChangeData &value) override
void setRenderTarget(RenderTarget target)
QRect adjusted(int dx1, int dy1, int dx2, int dy2) const const
bool isEmpty() const const
bool isValid() const const
void moveTopLeft(const QPoint &position)
void setCoords(int x1, int y1, int x2, int y2)
void setHeight(int height)
QPoint topLeft() const const
QRect toAlignedRect() const const
QRect boundingRect() const const
bool contains(const QPoint &p) const const
QPointF position() const const
QString & append(QChar ch)
bool isEmpty() const const
QString & prepend(QChar ch)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
void reserve(qsizetype size)
void resize(qsizetype newSize, QChar fillChar)
QByteArray toLocal8Bit() const const
qsizetype size() const const
virtual int styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const const=0
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
void keyEvent(KeyAction action, QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier, int delay)
void keySequence(QWidget *widget, const QKeySequence &keySequence)
bool isLocalFile() const const
QString path(ComponentFormattingOptions options) const const
QString toString(FormattingOptions options) const const
QPoint angleDelta() const const