2
This file is part of KTerminal, QML plugin of the Konsole,
3
which is a terminal emulator from KDE.
5
Copyright 2006-2008 by Robert Knight <robertknight@gmail.com>
6
Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
8
Rewritten for QT5/QML by Dmitry Zagnoyko <hiroshidi@gmail.com>, Copyright (C) 2013
10
This program is free software; you can redistribute it and/or modify
11
it under the terms of the GNU General Public License as published by
12
the Free Software Foundation; either version 2 of the License, or
13
(at your option) any later version.
15
This program is distributed in the hope that it will be useful,
16
but WITHOUT ANY WARRANTY; without even the implied warranty of
17
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
GNU General Public License for more details.
20
You should have received a copy of the GNU General Public License
21
along with this program; if not, write to the Free Software
22
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27
#include "TerminalDisplay.h"
30
#include <QtQuick/QtQuick>
32
#include <QGuiApplication>
33
#include <QStyleHints>
34
#include <QInputMethod>
36
#include <QtGui/QPainter>
37
#include <QtGui/QPixmap>
38
#include <QtGui/QClipboard>
39
#include <QtGui/QKeyEvent>
41
#include <QtCore/QEvent>
42
#include <QtCore/QTime>
43
#include <QtCore/QFile>
44
#include <QtCore/QTimer>
50
//#include <config-apps.h>
52
#include "konsole_wcwidth.h"
53
#include "ScreenWindow.h"
54
#include "ColorScheme.h"
55
#include "ColorTables.h"
56
#include "TerminalCharacterDecoder.h"
60
#define loc(X,Y) ((Y)*_columns+(X))
63
#define REPCHAR "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
64
"abcdefgjijklmnopqrstuvwxyz" \
67
const ColorEntry base_color_table[TABLE_COLORS] =
68
// The following are almost IBM standard color codes, with some slight
69
// gamma correction for the dim colors to compensate for bright X screens.
70
// It contains the 8 ansiterm/xterm colors in 2 intensities.
72
// Fixme: could add faint colors here, also.
74
ColorEntry(QColor(0x00,0x00,0x00), 0), ColorEntry( QColor(0xB2,0xB2,0xB2), 1), // Dfore, Dback
75
ColorEntry(QColor(0x00,0x00,0x00), 0), ColorEntry( QColor(0xB2,0x18,0x18), 0), // Black, Red
76
ColorEntry(QColor(0x18,0xB2,0x18), 0), ColorEntry( QColor(0xB2,0x68,0x18), 0), // Green, Yellow
77
ColorEntry(QColor(0x18,0x18,0xB2), 0), ColorEntry( QColor(0xB2,0x18,0xB2), 0), // Blue, Magenta
78
ColorEntry(QColor(0x18,0xB2,0xB2), 0), ColorEntry( QColor(0xB2,0xB2,0xB2), 0), // Cyan, White
80
ColorEntry(QColor(0x00,0x00,0x00), 0), ColorEntry( QColor(0xFF,0xFF,0xFF), 1),
81
ColorEntry(QColor(0x68,0x68,0x68), 0), ColorEntry( QColor(0xFF,0x54,0x54), 0),
82
ColorEntry(QColor(0x54,0xFF,0x54), 0), ColorEntry( QColor(0xFF,0xFF,0x54), 0),
83
ColorEntry(QColor(0x54,0x54,0xFF), 0), ColorEntry( QColor(0xFF,0x54,0xFF), 0),
84
ColorEntry(QColor(0x54,0xFF,0xFF), 0), ColorEntry( QColor(0xFF,0xFF,0xFF), 0)
87
// scroll increment used when dragging selection at top/bottom of window.
90
bool KTerminalDisplay::_antialiasText = true;
91
bool KTerminalDisplay::HAVE_TRANSPARENCY = true;
93
// we use this to force QPainter to display text in LTR mode
94
// more information can be found in: http://unicode.org/reports/tr9/
95
const QChar LTR_OVERRIDE_CHAR( 0x202D );
97
/* ------------------------------------------------------------------------- */
101
/* ------------------------------------------------------------------------- */
103
/* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb)
106
----------- ------- ------- ------- ------- ------- ------- ------- -------
107
ANSI (bgr) Black Red Green Yellow Blue Magenta Cyan White
108
IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White
113
/////////////////////////////////////////////////////////////////////////////////////
114
/////////////////////////////////////////////////////////////////////////////////////
116
/////////////////////////////////////////////////////////////////////////////////////
117
/////////////////////////////////////////////////////////////////////////////////////
120
/* ------------------------------------------------------------------------- */
122
/* Constructor / Destructor */
124
/* ------------------------------------------------------------------------- */
125
KTerminalDisplay::KTerminalDisplay(QQuickItem *parent) :
126
QQuickPaintedItem(parent)
144
,_wordSelectionMode(false)
145
,_lineSelectionMode(false)
146
,_preserveLineBreaks(false)
147
,_columnSelectionMode(false)
148
,_wordCharacters(":@-./_~")
149
,_bellMode(NotifyBell) //def: SystemBeepBell)
152
,_cursorBlinking(false)
153
,_hasBlinkingCursor(false)
154
,_allowBlinkingText(true)
157
,_flowControlWarningEnabled(false)
159
,_colorsInverted(false)
160
,_cursorShape(BlockCursor)
162
,m_focusOnClick(true)
163
,m_showVKBonClick(true)
166
_blendColor = qRgba(0,0,0,0xff);
167
m_widgetRect = QRectF(0,0,1,1);
169
m_palette = qApp->palette();
171
m_font = QFont("Monospace",16);
174
#if QT_VERSION >= 0x040700
175
m_font.setStyleStrategy(QFont::ForceIntegerMetrics);
177
#warning "Correct handling of the QFont metrics requited Qt>=4.7"
181
// The offsets are not yet calculated.
182
// Do not calculate these too often to be more smoothly when resizing
183
// konsole in opaque mode.
184
_topMargin = DEFAULT_TOP_MARGIN;
185
_leftMargin = DEFAULT_LEFT_MARGIN;
187
// setup timers for blinking cursor and text
188
_blinkTimer = new QTimer(this);
189
connect(_blinkTimer, SIGNAL(timeout()), this, SLOT(blinkEvent()));
190
_blinkCursorTimer = new QTimer(this);
191
connect(_blinkCursorTimer, SIGNAL(timeout()), this, SLOT(blinkCursorEvent()));
193
//KCursor::setAutoHideCursor( this, true );
195
setColorTable(base_color_table);
197
new AutoScrollHandler(this);
199
setAcceptedMouseButtons(Qt::LeftButton);
200
setFlags(ItemHasContents | ItemAcceptsInputMethod);
201
//installEventFilter(this);
203
m_font.setStyleHint(QFont::TypeWriter);
208
KTerminalDisplay::~KTerminalDisplay()
210
disconnect(_blinkTimer);
211
disconnect(_blinkCursorTimer);
216
void KTerminalDisplay::setSession(KSession * session)
218
if (m_session != session) {
221
connect(this, SIGNAL(copyAvailable(bool)),
222
m_session, SLOT(selectionChanged(bool)));
223
connect(this, SIGNAL(termGetFocus()),
224
m_session, SIGNAL(termGetFocus()));
225
connect(this, SIGNAL(termLostFocus()),
226
m_session, SIGNAL(termLostFocus()));
227
connect(this, SIGNAL(keyPressedSignal(QKeyEvent *)),
228
m_session, SIGNAL(termKeyPressed(QKeyEvent *)));
230
m_session->addView(this);
232
//m_session->changeDir(QDir::currentPath());
234
setRandomSeed(m_session->getRandomSeed());
236
emit changedSession(session);
241
ScreenWindow* KTerminalDisplay::screenWindow() const
243
return _screenWindow;
246
void KTerminalDisplay::forcedFocus()
249
bool focused = hasActiveFocus();
253
focused = hasActiveFocus();
258
void KTerminalDisplay::setScreenWindow(ScreenWindow* window)
260
// disconnect existing screen window if any
263
disconnect( _screenWindow , 0 , this , 0 );
266
_screenWindow = window;
271
// TODO: Determine if this is an issue.
272
//#warning "The order here is not specified - does it matter whether updateImage or updateLineProperties comes first?"
273
connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateLineProperties()) );
274
connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateImage()) );
275
window->setWindowLines(_lines);
279
const ColorEntry* KTerminalDisplay::colorTable() const
283
void KTerminalDisplay::setBackgroundColor(const QColor& color)
285
_colorTable[DEFAULT_BACK_COLOR].color = color;
286
// QPalette p = m_palette;
287
// p.setColor( backgroundRole(), color );
292
void KTerminalDisplay::setForegroundColor(const QColor& color)
294
_colorTable[DEFAULT_FORE_COLOR].color = color;
298
void KTerminalDisplay::setColorTable(const ColorEntry table[])
300
for (int i = 0; i < TABLE_COLORS; i++)
301
_colorTable[i] = table[i];
303
setBackgroundColor(_colorTable[DEFAULT_BACK_COLOR].color);
307
/* ------------------------------------------------------------------------- */
311
/* ------------------------------------------------------------------------- */
314
The VT100 has 32 special graphical characters. The usual vt100 extended
315
xterm fonts have these at 0x00..0x1f.
317
QT's iso mapping leaves 0x00..0x7f without any changes. But the graphicals
318
come in here as proper unicode characters.
320
We treat non-iso10646 fonts as VT100 extended and do the requiered mapping
321
from unicode to 0x00..0x1f. The remaining translation is then left to the
325
static inline bool isLineChar(quint16 c) { return ((c & 0xFF80) == 0x2500);}
326
static inline bool isLineCharString(const QString& string)
328
return (string.length() > 0) && (isLineChar(string.at(0).unicode()));
332
// assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i.
334
unsigned short vt100_graphics[32] =
335
{ // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15
336
0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0,
337
0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c,
338
0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534,
339
0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7
342
void KTerminalDisplay::fontChange(const QFont&)
344
QFontMetrics fm(m_font);
345
_fontHeight = fm.height() + _lineSpacing;
347
// waba TerminalDisplay 1.123:
348
// "Base character width on widest ASCII character. This prevents too wide
349
// characters in the presence of double wide (e.g. Japanese) characters."
350
// Get the width from representative normal width characters
351
_fontWidth = (double)fm.width(REPCHAR)/(double)strlen(REPCHAR);
355
int fw = fm.width(REPCHAR[0]);
356
for(unsigned int i=1; i< strlen(REPCHAR); i++)
358
if (fw != fm.width(REPCHAR[i]))
368
_fontAscent = fm.ascent();
370
emit changedFontMetricSignal( _fontHeight, _fontWidth );
375
void KTerminalDisplay::setVTFont(const QFont& f)
379
#if defined(Q_WS_MAC) || defined(Q_WS_UBUNTU)
380
#if QT_VERSION >= 0x040700
381
font.setStyleStrategy(QFont::ForceIntegerMetrics);
383
#warning "Correct handling of the QFont metrics requited Qt>=4.7"
387
QFontMetrics metrics(font);
389
if ( !QFontInfo(font).fixedPitch() )
391
qDebug() << "Using an unsupported variable-width font in the terminal. This may produce display errors.";
394
if ( metrics.height() < height() && metrics.maxWidth() < width() )
396
// hint that text should be drawn without anti-aliasing.
397
// depending on the user's font configuration, this may not be respected
399
font.setStyleStrategy( QFont::NoAntialias );
401
// experimental optimization. Konsole assumes that the terminal is using a
402
// mono-spaced font, in which case kerning information should have an effect.
403
// Disabling kerning saves some computation when rendering text.
404
font.setKerning(false);
406
//QWidget::setFont(font);
412
void KTerminalDisplay::setColorScheme(const QString &name)
414
const ColorScheme *cs;
415
// avoid legacy (int) solution
416
if (!availableColorSchemes().contains(name))
417
cs = ColorSchemeManager::instance()->defaultColorScheme();
419
cs = ColorSchemeManager::instance()->findColorScheme(name);
424
qDebug() << "Cannot load color scheme: " << name;
429
ColorEntry table[TABLE_COLORS];
430
cs->getColorTable(table);
431
setColorTable(table);
435
QStringList KTerminalDisplay::availableColorSchemes()
438
foreach (const ColorScheme* cs, ColorSchemeManager::instance()->allColorSchemes())
439
ret.append(cs->name());
443
void KTerminalDisplay::click(qreal x, qreal y)
445
QMouseEvent me(QEvent::MouseButtonPress, QPointF(x,y), Qt::RightButton, Qt::RightButton, Qt::NoModifier);
446
mousePressEvent(&me);
449
void KTerminalDisplay::setAutoFocus(bool au)
452
emit changedAutoFocus(au);
455
void KTerminalDisplay::setAutoVKB(bool au)
457
m_showVKBonClick = au;
458
emit changedAutoVKB(au);
462
/* ------------------------------------------------------------------------- */
464
/* Display Operations */
466
/* ------------------------------------------------------------------------- */
469
A table for emulating the simple (single width) unicode drawing chars.
470
It represents the 250x - 257x glyphs. If it's zero, we can't use it.
471
if it's not, it's encoded as follows: imagine a 5x5 grid where the points are numbered
472
0 to 24 left to top, top to bottom. Each point is represented by the corresponding bit.
474
Then, the pixels basically have the following interpretation:
516
#include "LineFont.h"
518
static void drawLineChar(QPainter* paint, qreal x, qreal y, qreal w, qreal h, uchar code)
520
//Calculate cell midpoints, end points.
523
qreal ex = x + w - 1;
524
qreal ey = y + h - 1;
526
quint32 toDraw = LineChars[code];
530
paint->drawLine(cx-1, y, cx-1, cy-2);
532
paint->drawLine(cx, y, cx, cy-2);
534
paint->drawLine(cx+1, y, cx+1, cy-2);
538
paint->drawLine(cx-1, cy+2, cx-1, ey);
540
paint->drawLine(cx, cy+2, cx, ey);
542
paint->drawLine(cx+1, cy+2, cx+1, ey);
546
paint->drawLine(x, cy-1, cx-2, cy-1);
548
paint->drawLine(x, cy, cx-2, cy);
550
paint->drawLine(x, cy+1, cx-2, cy+1);
554
paint->drawLine(cx+2, cy-1, ex, cy-1);
556
paint->drawLine(cx+2, cy, ex, cy);
558
paint->drawLine(cx+2, cy+1, ex, cy+1);
560
//Intersection points.
562
paint->drawPoint(cx-1, cy-1);
564
paint->drawPoint(cx, cy-1);
566
paint->drawPoint(cx+1, cy-1);
569
paint->drawPoint(cx-1, cy);
571
paint->drawPoint(cx, cy);
573
paint->drawPoint(cx+1, cy);
576
paint->drawPoint(cx-1, cy+1);
578
paint->drawPoint(cx, cy+1);
580
paint->drawPoint(cx+1, cy+1);
584
void KTerminalDisplay::setKeyboardCursorShape(KeyboardCursorShape shape)
586
_cursorShape = shape;
589
KTerminalDisplay::KeyboardCursorShape KTerminalDisplay::keyboardCursorShape() const
594
void KTerminalDisplay::setKeyboardCursorColor(bool useForegroundColor, const QColor& color)
596
if (useForegroundColor)
597
_cursorColor = QColor(); // an invalid color means that
598
// the foreground color of the
599
// current character should
603
_cursorColor = color;
606
QColor KTerminalDisplay::keyboardCursorColor() const
611
void KTerminalDisplay::ShowVKB(bool show)
613
bool focused = hasActiveFocus();
615
if (focused && show && !qGuiApp->inputMethod()->isVisible()) {
616
updateInputMethod(Qt::ImEnabled);
617
qGuiApp->inputMethod()->show();
620
if (focused && !show && qGuiApp->inputMethod()->isVisible()) {
621
updateInputMethod(Qt::ImEnabled);
622
qGuiApp->inputMethod()->hide();
626
void KTerminalDisplay::setRandomSeed(uint randomSeed) { _randomSeed = randomSeed; }
627
uint KTerminalDisplay::randomSeed() const { return _randomSeed; }
633
void TerminalDisplay::setCursorPos(const int curx, const int cury)
636
ypos = _topMargin + _fontHeight*(cury-1) + _fontAscent;
637
xpos = _leftMargin + _fontWidth*curx;
638
//setMicroFocusHint(xpos, ypos, 0, _fontHeight); //### ???
639
// fprintf(stderr, "x/y = %d/%d\txpos/ypos = %d/%d\n", curx, cury, xpos, ypos);
645
// scrolls the image by 'lines', down if lines > 0 or up otherwise.
647
// the terminal emulation keeps track of the scrolling of the character
648
// image as it receives input, and when the view is updated, it calls scrollImage()
649
// with the final scroll amount. this improves performance because scrolling the
650
// display is much cheaper than re-rendering all the text for the
651
// part of the image which has moved up or down.
652
// Instead only new lines have to be drawn
653
void KTerminalDisplay::scrollImage(int lines , const QRect& screenWindowRegion)
655
// constrain the region to the display
656
// the bottom of the region is capped to the number of lines in the display's
657
// internal image - 2, so that the height of 'region' is strictly less
658
// than the height of the internal image.
659
QRect region = screenWindowRegion;
660
region.setBottom( qMin(region.bottom(),this->_lines-2) );
662
// return if there is nothing to do
666
|| (region.top() + abs(lines)) >= region.bottom()
667
|| this->_lines <= region.height() ) return;
670
void* firstCharPos = &_image[ region.top() * this->_columns ];
671
void* lastCharPos = &_image[ (region.top() + abs(lines)) * this->_columns ];
673
int top = _topMargin + (region.top() * _fontHeight);
674
Q_UNUSED(top) //BAD IDEA!
675
int linesToMove = region.height() - abs(lines);
676
int bytesToMove = linesToMove *
680
Q_ASSERT( linesToMove > 0 );
681
Q_ASSERT( bytesToMove > 0 );
683
//scroll internal image
686
// check that the memory areas that we are going to move are valid
687
Q_ASSERT( (char*)lastCharPos + bytesToMove <
688
(char*)(_image + (this->_lines * this->_columns)) );
690
Q_ASSERT( (lines*this->_columns) < _imageSize );
692
//scroll internal image down
693
memmove( firstCharPos , lastCharPos , bytesToMove );
697
// check that the memory areas that we are going to move are valid
698
Q_ASSERT( (char*)firstCharPos + bytesToMove <
699
(char*)(_image + (this->_lines * this->_columns)) );
701
//scroll internal image up
702
memmove( lastCharPos , firstCharPos , bytesToMove );
705
// Q_ASSERT(scrollRect.isValid() && !scrollRect.isEmpty());
707
//scroll the display vertically to match internal _image
708
// scroll( 0 , _fontHeight * (-lines) , scrollRect );
711
void KTerminalDisplay::updateImage()
713
if ( !_screenWindow )
716
// optimization - scroll the existing image where possible and
717
// avoid expensive text drawing for parts of the image that
718
// can simply be moved up or down
719
scrollImage( _screenWindow->scrollCount() ,
720
_screenWindow->scrollRegion() );
721
_screenWindow->resetScrollCount();
725
// The emitted changedContentSizeSignal also leads to getImage being recreated, so do this first.
729
Character* const newimg = _screenWindow->getImage();
730
int lines = _screenWindow->windowLines();
731
int columns = _screenWindow->windowColumns();
733
Q_ASSERT( this->_usedLines <= this->_lines );
734
Q_ASSERT( this->_usedColumns <= this->_columns );
740
CharacterColor cf; // undefined
741
CharacterColor _clipboard; // undefined
742
int cr = -1; // undefined
744
const int linesToUpdate = qMin(this->_lines, qMax(0,lines ));
745
const int columnsToUpdate = qMin(this->_columns,qMax(0,columns));
747
QChar *disstrU = new QChar[columnsToUpdate];
748
char *dirtyMask = new char[columnsToUpdate+2];
751
// debugging variable, this records the number of lines that are found to
752
// be 'dirty' ( ie. have changed from the old _image to the new _image ) and
753
// which therefore need to be repainted
754
int dirtyLineCount = 0;
756
for (y = 0; y < linesToUpdate; ++y)
758
const Character* currentLine = &_image[y*this->_columns];
759
const Character* const newLine = &newimg[y*columns];
761
bool updateLine = false;
763
// The dirty mask indicates which characters need repainting. We also
764
// mark surrounding neighbours dirty, in case the character exceeds
765
// its cell boundaries
766
memset(dirtyMask, 0, columnsToUpdate+2);
768
for( x = 0 ; x < columnsToUpdate ; ++x)
770
if ( newLine[x] != currentLine[x] )
776
if (!_resizing) // not while _resizing, we're expecting a paintEvent
777
for (x = 0; x < columnsToUpdate; ++x)
779
_hasBlinker |= (newLine[x].rendition & RE_BLINK);
781
// Start drawing if this character or the next one differs.
782
// We also take the next one into account to handle the situation
783
// where characters exceed their cell width.
786
quint16 c = newLine[x+0].character;
790
disstrU[p++] = c; //fontMap(c);
791
bool lineDraw = isLineChar(c);
792
bool doubleWidth = (x+1 == columnsToUpdate) ? false : (newLine[x+1].character == 0);
793
cr = newLine[x].rendition;
794
_clipboard = newLine[x].backgroundColor;
795
if (newLine[x].foregroundColor != cf) cf = newLine[x].foregroundColor;
796
int lln = columnsToUpdate - x;
797
for (len = 1; len < lln; ++len)
799
const Character& ch = newLine[x+len];
802
continue; // Skip trailing part of multi-col chars.
804
bool nextIsDoubleWidth = (x+len+1 == columnsToUpdate) ? false : (newLine[x+len+1].character == 0);
806
if ( ch.foregroundColor != cf ||
807
ch.backgroundColor != _clipboard ||
808
ch.rendition != cr ||
810
isLineChar(c) != lineDraw ||
811
nextIsDoubleWidth != doubleWidth )
814
disstrU[p++] = c; //fontMap(c);
817
QString unistr(disstrU, p);
819
bool saveFixedFont = _fixedFont;
827
_fixedFont = saveFixedFont;
833
//both the top and bottom halves of double height _lines must always be redrawn
834
//although both top and bottom halves contain the same characters, only
835
//the top one is actually
837
if (_lineProperties.count() > y)
838
updateLine |= (_lineProperties[y] & LINE_DOUBLEHEIGHT);
840
// if the characters on the line are different in the old and the new _image
841
// then this line must be repainted.
846
// add the area occupied by this line to the region which needs to be
848
QRect dirtyRect = QRect( qRound(_leftMargin),
849
qRound(_topMargin + _fontHeight*y) ,
850
qRound(_fontWidth * columnsToUpdate) ,
853
dirtyRegion |= dirtyRect;
856
// replace the line of characters in the old _image with the
857
// current line of the new _image
858
memcpy((void*)currentLine,(const void*)newLine,columnsToUpdate*sizeof(Character));
861
// if the new _image is smaller than the previous _image, then ensure that the area
862
// outside the new _image is cleared
863
if ( linesToUpdate < _usedLines )
865
dirtyRegion |= QRect( qRound(_leftMargin),
866
qRound(_topMargin + _fontHeight*linesToUpdate) ,
867
qRound(_fontWidth * this->_columns) ,
868
_fontHeight * (_usedLines-linesToUpdate) );
870
_usedLines = linesToUpdate;
872
if ( columnsToUpdate < _usedColumns )
874
dirtyRegion |= QRect( qRound(_leftMargin + columnsToUpdate*_fontWidth) ,
876
qRound(_fontWidth * (_usedColumns-columnsToUpdate)) ,
877
_fontHeight * this->_lines );
879
_usedColumns = columnsToUpdate;
881
dirtyRegion |= geometryRound(_inputMethodData.previousPreeditRect);
883
// update the parts of the display which have changed
884
// update(dirtyRegion.boundingRect());
886
// update whole widget
889
if ( _hasBlinker && !_blinkTimer->isActive()) _blinkTimer->start( TEXT_BLINK_DELAY );
890
if (!_hasBlinker && _blinkTimer->isActive()) { _blinkTimer->stop(); _blinking = false; }
896
void KTerminalDisplay::setBlinkingCursor(bool blink)
898
_hasBlinkingCursor=blink;
900
if (blink && !_blinkCursorTimer->isActive())
901
_blinkCursorTimer->start(100); // WARN! HARDCODE
903
if (!blink && _blinkCursorTimer->isActive())
905
_blinkCursorTimer->stop();
909
_cursorBlinking = false;
913
void KTerminalDisplay::setBlinkingTextEnabled(bool blink)
915
_allowBlinkingText = blink;
917
if (blink && !_blinkTimer->isActive())
918
_blinkTimer->start(qApp->styleHints()->cursorFlashTime() / 2);
920
if (!blink && _blinkTimer->isActive())
927
void KTerminalDisplay::focusOutEvent(QFocusEvent*)
929
emit termLostFocus();
930
// trigger a repaint of the cursor so that it is both visible (in case
931
// it was hidden during blinking)
932
// and drawn in a focused out state
933
_cursorBlinking = false;
936
_blinkCursorTimer->stop();
942
//parent->activeFocus = false;
943
emit activeFocusChanged(true);
946
void KTerminalDisplay::focusInEvent(QFocusEvent*)
949
if (_hasBlinkingCursor)
951
_blinkCursorTimer->start();
956
_blinkTimer->start();
959
QPoint KTerminalDisplay::cursorPosition() const
962
return _screenWindow->cursorPosition();
967
void KTerminalDisplay::blinkEvent()
969
if (!_allowBlinkingText) return;
971
_blinking = !_blinking;
973
//TODO: Optimize to only repaint the areas of the widget
974
// where there is blinking text
975
// rather than repainting the whole widget.
979
void KTerminalDisplay::updateCursor()
981
QRect cursorRect = imageToWidget( QRect(cursorPosition(),QSize(1,1)) );
985
void KTerminalDisplay::blinkCursorEvent()
987
_cursorBlinking = !_cursorBlinking;
991
/* ------------------------------------------------------------------------- */
995
/* ------------------------------------------------------------------------- */
997
void KTerminalDisplay::propagateSize()
1004
void KTerminalDisplay::updateImageSize()
1006
Character* oldimg = _image;
1007
int oldlin = _lines;
1008
int oldcol = _columns;
1012
// copy the old image to reduce flicker
1013
int lines = qMin(oldlin,_lines);
1014
int columns = qMin(oldcol,_columns);
1018
for (int line = 0; line < lines; line++)
1020
memcpy((void*)&_image[_columns*line],
1021
(void*)&oldimg[oldcol*line],columns*sizeof(Character));
1027
_screenWindow->setWindowLines(_lines);
1029
_resizing = (oldlin!=_lines) || (oldcol!=_columns);
1033
emit changedContentSizeSignal(_contentHeight, _contentWidth); // expose resizeEvent
1039
void KTerminalDisplay::scrollToEnd()
1041
//_screenWindow->scrollTo( _scrollBar->value() + 1 );
1042
_screenWindow->setTrackOutput( _screenWindow->atEndOfOutput() );
1045
void KTerminalDisplay::extendSelection( const QPoint& position )
1047
QPoint pos = position;
1049
if ( !_screenWindow )
1052
//if ( !contentsRect().contains(ev->pos()) ) return;
1054
// we're in the process of moving the mouse with the left button pressed
1055
// the mouse cursor will kept caught within the bounds of the text in
1058
int linesBeyondWidget = 0;
1060
QRect textBounds( _leftMargin,
1062
_usedColumns*_fontWidth-1,
1063
_usedLines*_fontHeight-1);
1065
// Adjust position within text area bounds.
1066
QPoint oldpos = pos;
1068
pos.setX( qBound(textBounds.left(),pos.x(),textBounds.right()) );
1069
pos.setY( qBound(textBounds.top(),pos.y(),textBounds.bottom()) );
1071
if ( oldpos.y() > textBounds.bottom() )
1073
linesBeyondWidget = (oldpos.y()-textBounds.bottom()) / _fontHeight;
1075
if ( oldpos.y() < textBounds.top() )
1077
linesBeyondWidget = (textBounds.top()-oldpos.y()) / _fontHeight;
1082
getCharacterPosition(pos,charLine,charColumn);
1084
QPoint here = QPoint(charColumn,charLine); //QPoint((pos.x()-tLx-_leftMargin+(_fontWidth/2))/_fontWidth,(pos.y()-tLy-_topMargin)/_fontHeight);
1086
QPoint _iPntSelCorr = _iPntSel;
1087
_iPntSelCorr.ry() -= 0; //_scrollBar->value();
1088
QPoint _pntSelCorr = _pntSel;
1089
_pntSelCorr.ry() -= 0; //_scrollBar->value();
1090
bool swapping = false;
1092
if ( _wordSelectionMode )
1094
// Extend to word boundaries
1098
bool left_not_right = ( here.y() < _iPntSelCorr.y() ||
1099
( here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() ) );
1100
bool old_left_not_right = ( _pntSelCorr.y() < _iPntSelCorr.y() ||
1101
( _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() ) );
1102
swapping = left_not_right != old_left_not_right;
1104
// Find left (left_not_right ? from here : from start)
1105
QPoint left = left_not_right ? here : _iPntSelCorr;
1106
i = loc(left.x(),left.y());
1107
if (i>=0 && i<=_imageSize) {
1108
selClass = charClass(_image[i].character);
1109
while ( ((left.x()>0) || (left.y()>0 && (_lineProperties[left.y()-1] & LINE_WRAPPED) ))
1110
&& charClass(_image[i-1].character) == selClass )
1111
{ i--; if (left.x()>0) left.rx()--; else {left.rx()=_usedColumns-1; left.ry()--;} }
1114
// Find left (left_not_right ? from start : from here)
1115
QPoint right = left_not_right ? _iPntSelCorr : here;
1116
i = loc(right.x(),right.y());
1117
if (i>=0 && i<=_imageSize) {
1118
selClass = charClass(_image[i].character);
1119
while( ((right.x()<_usedColumns-1) || (right.y()<_usedLines-1 && (_lineProperties[right.y()] & LINE_WRAPPED) ))
1120
&& charClass(_image[i+1].character) == selClass )
1121
{ i++; if (right.x()<_usedColumns-1) right.rx()++; else {right.rx()=0; right.ry()++; } }
1124
// Pick which is start (ohere) and which is extension (here)
1125
if ( left_not_right )
1127
here = left; ohere = right;
1131
here = right; ohere = left;
1136
if ( _lineSelectionMode )
1138
// Extend to complete line
1139
bool above_not_below = ( here.y() < _iPntSelCorr.y() );
1141
QPoint above = above_not_below ? here : _iPntSelCorr;
1142
QPoint below = above_not_below ? _iPntSelCorr : here;
1144
while (above.y()>0 && (_lineProperties[above.y()-1] & LINE_WRAPPED) )
1146
while (below.y()<_usedLines-1 && (_lineProperties[below.y()] & LINE_WRAPPED) )
1150
below.setX(_usedColumns-1);
1152
// Pick which is start (ohere) and which is extension (here)
1153
if ( above_not_below )
1155
here = above; ohere = below;
1159
here = below; ohere = above;
1162
QPoint newSelBegin = QPoint( ohere.x(), ohere.y() );
1163
swapping = !(_tripleSelBegin==newSelBegin);
1164
_tripleSelBegin = newSelBegin;
1170
if ( !_wordSelectionMode && !_lineSelectionMode )
1175
bool left_not_right = ( here.y() < _iPntSelCorr.y() ||
1176
( here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() ) );
1177
bool old_left_not_right = ( _pntSelCorr.y() < _iPntSelCorr.y() ||
1178
( _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() ) );
1179
swapping = left_not_right != old_left_not_right;
1181
// Find left (left_not_right ? from here : from start)
1182
QPoint left = left_not_right ? here : _iPntSelCorr;
1184
// Find left (left_not_right ? from start : from here)
1185
QPoint right = left_not_right ? _iPntSelCorr : here;
1186
if ( right.x() > 0 && !_columnSelectionMode )
1188
i = loc(right.x(),right.y());
1189
if (i>=0 && i<=_imageSize) {
1190
selClass = charClass(_image[i-1].character);
1191
/* if (selClass == ' ')
1193
while ( right.x() < _usedColumns-1 && charClass(_image[i+1].character) == selClass && (right.y()<_usedLines-1) &&
1194
!(_lineProperties[right.y()] & LINE_WRAPPED))
1195
{ i++; right.rx()++; }
1196
if (right.x() < _usedColumns-1)
1197
right = left_not_right ? _iPntSelCorr : here;
1199
right.rx()++; // will be balanced later because of offset=-1;
1204
// Pick which is start (ohere) and which is extension (here)
1205
if ( left_not_right )
1207
here = left; ohere = right; offset = 0;
1211
here = right; ohere = left; offset = -1;
1215
//if ((here == _pntSelCorr) && (scroll == _scrollBar->value())) return; // not moved
1217
if (here == ohere) return; // It's not left, it's not right.
1219
if ( _actSel < 2 || swapping )
1221
if ( _columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode )
1223
_screenWindow->setSelectionStart( ohere.x() , ohere.y() , true );
1227
_screenWindow->setSelectionStart( ohere.x()-1-offset , ohere.y() , false );
1232
_actSel = 2; // within selection
1234
_pntSel.ry() += 0; //_scrollBar->value();
1236
if ( _columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode )
1238
_screenWindow->setSelectionEnd( here.x() , here.y() );
1242
_screenWindow->setSelectionEnd( here.x()+offset , here.y() );
1245
Q_UNUSED(linesBeyondWidget)
1249
void KTerminalDisplay::updateLineProperties()
1251
if ( !_screenWindow )
1254
_lineProperties = _screenWindow->getLineProperties();
1257
QChar KTerminalDisplay::charClass(QChar qch) const
1259
if ( qch.isSpace() ) return ' ';
1261
if ( qch.isLetterOrNumber() || _wordCharacters.contains(qch, Qt::CaseInsensitive ) )
1267
void KTerminalDisplay::setWordCharacters(const QString& wc)
1269
_wordCharacters = wc;
1272
/* ------------------------------------------------------------------------- */
1276
/* ------------------------------------------------------------------------- */
1280
void KTerminalDisplay::emitSelection(bool useXselection,bool appendReturn)
1282
if ( !_screenWindow )
1285
// Paste Clipboard by simulating keypress events
1286
QString text = QGuiApplication::clipboard()->text(useXselection ? QClipboard::Selection :
1287
QClipboard::Clipboard);
1290
if ( ! text.isEmpty() )
1292
text.replace('\n', '\r');
1293
QKeyEvent e(QEvent::KeyPress, 0, Qt::NoModifier, text);
1294
emit keyPressedSignal(&e); // expose as a big fat keypress event
1296
_screenWindow->clearSelection();
1300
void KTerminalDisplay::setSelection(const QString& t)
1302
QGuiApplication::clipboard()->setText(t, QClipboard::Selection);
1305
void KTerminalDisplay::copyClipboard()
1307
if ( !_screenWindow )
1310
QString text = _screenWindow->selectedText(_preserveLineBreaks);
1311
if (!text.isEmpty())
1312
QGuiApplication::clipboard()->setText(text);
1315
void KTerminalDisplay::pasteClipboard()
1317
emitSelection(false,false);
1320
void KTerminalDisplay::pasteSelection()
1322
emitSelection(true,false);
1325
/* ------------------------------------------------------------------------- */
1329
/* ------------------------------------------------------------------------- */
1331
void KTerminalDisplay::setFlowControlWarningEnabled( bool enable )
1333
_flowControlWarningEnabled = enable;
1335
// if the dialog is currently visible and the flow control warning has
1336
// been disabled then hide the dialog
1338
// outputSuspended(false);
1341
void KTerminalDisplay::inputMethodEvent( QInputMethodEvent* event )
1343
QKeyEvent keyEvent(QEvent::KeyPress,0,Qt::NoModifier,event->commitString());
1344
emit keyPressedSignal(&keyEvent);
1346
_inputMethodData.preeditString = event->preeditString();
1347
QRect updRect = geometryRound(preeditRect() | _inputMethodData.previousPreeditRect);
1353
void KTerminalDisplay::inputMethodQuery(QInputMethodQueryEvent *event)
1355
event->setValue(Qt::ImEnabled, true);
1356
event->setValue(Qt::ImHints, Qt::ImhNoPredictiveText);
1360
QVariant KTerminalDisplay::inputMethodQuery(Qt::InputMethodQuery query) const
1362
const QPoint cursorPos = _screenWindow ? _screenWindow->cursorPosition() : QPoint(0,0);
1366
return (bool)(flags() & ItemAcceptsInputMethod);
1368
case Qt::ImMicroFocus:
1369
return imageToWidget(QRect(cursorPos.x(),cursorPos.y(),1,1));
1374
case Qt::ImCursorPosition:
1375
// return the cursor position within the current line
1376
return cursorPos.x();
1378
case Qt::ImSurroundingText:
1380
// return the text from the current line
1382
QTextStream stream(&lineText);
1383
PlainTextDecoder decoder;
1384
decoder.begin(&stream);
1385
decoder.decodeLine(&_image[loc(0,cursorPos.y())],_usedColumns,_lineProperties[cursorPos.y()]);
1390
case Qt::ImCurrentSelection:
1400
bool KTerminalDisplay::handleShortcutOverrideEvent(QKeyEvent* keyEvent)
1402
int modifiers = keyEvent->modifiers();
1404
// When a possible shortcut combination is pressed,
1405
// emit the overrideShortcutCheck() signal to allow the host
1406
// to decide whether the terminal should override it or not.
1407
if (modifiers != Qt::NoModifier)
1409
int modifierCount = 0;
1410
unsigned int currentModifier = Qt::ShiftModifier;
1412
while (currentModifier <= Qt::KeypadModifier)
1414
if (modifiers & currentModifier)
1416
currentModifier <<= 1;
1418
if (modifierCount < 2)
1420
bool override = false;
1421
emit overrideShortcutCheck(keyEvent,override);
1430
// Override any of the following shortcuts because
1431
// they are needed by the terminal
1432
int keyCode = keyEvent->key() | modifiers;
1435
// list is taken from the QLineEdit::event() code
1437
case Qt::Key_Delete:
1440
case Qt::Key_Backspace:
1449
bool KTerminalDisplay::event(QEvent* event)
1451
bool eventHandled = false;
1452
switch (event->type())
1454
case QEvent::ShortcutOverride:
1455
eventHandled = handleShortcutOverrideEvent((QKeyEvent*)event);
1457
case QEvent::PaletteChange:
1458
case QEvent::ApplicationPaletteChange:
1460
case QEvent::InputMethod:
1461
inputMethodEvent(static_cast<QInputMethodEvent *>(event));
1463
case QEvent::InputMethodQuery:
1464
inputMethodQuery(static_cast<QInputMethodQueryEvent *>(event));
1467
eventHandled = QQuickPaintedItem::event(event);
1470
return eventHandled; //parent->event(event);
1473
void KTerminalDisplay::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1475
if (newGeometry != oldGeometry) {
1476
m_widgetRect = newGeometry;
1481
QQuickPaintedItem::geometryChanged(newGeometry,oldGeometry);
1484
QRect KTerminalDisplay::geometryRound(const QRectF &r) const
1488
rect.setTop(qRound(r.top()));
1489
rect.setBottom(qRound(r.bottom()));
1490
rect.setLeft(qRound(r.left()));
1491
rect.setRight(qRound(r.right()));
1496
void KTerminalDisplay::mousePressEvent(QMouseEvent *ev)
1498
if (m_focusOnClick) forcedFocus();
1499
if (m_showVKBonClick) ShowVKB(true);
1502
QQuickPaintedItem::mouseMoveEvent(ev);
1505
void KTerminalDisplay::setBellMode(int mode)
1510
void KTerminalDisplay::enableBell()
1515
void KTerminalDisplay::bell(const QString& message)
1519
if (_bellMode==NoBell) return;
1521
//limit the rate at which bells can occur
1522
//...mainly for sound effects where rapid bells in sequence
1523
//produce a horrible noise
1527
QTimer::singleShot(500,this,SLOT(enableBell()));
1529
if (_bellMode==SystemBeepBell)
1532
//QGuiApplication::beep();
1536
else if (_bellMode==NotifyBell)
1538
//KNotification::event("BellVisible", message,QPixmap(),this);
1539
// TODO/FIXME: qt4 notifications?
1541
else if (_bellMode==VisualBell)
1544
QTimer::singleShot(200,this,SLOT(swapColorTable()));
1549
void KTerminalDisplay::selectionChanged()
1551
emit copyAvailable(_screenWindow->selectedText(false).isEmpty() == false);
1554
void KTerminalDisplay::swapColorTable()
1556
ColorEntry color = _colorTable[1];
1557
_colorTable[1]=_colorTable[0];
1558
_colorTable[0]= color;
1559
_colorsInverted = !_colorsInverted;
1563
void KTerminalDisplay::clearImage()
1565
// We initialize _image[_imageSize] too. See makeImage()
1566
for (int i = 0; i <= _imageSize; i++)
1568
_image[i].character = ' ';
1569
_image[i].foregroundColor = CharacterColor(COLOR_SPACE_DEFAULT,
1570
DEFAULT_FORE_COLOR);
1571
_image[i].backgroundColor = CharacterColor(COLOR_SPACE_DEFAULT,
1572
DEFAULT_BACK_COLOR);
1573
_image[i].rendition = DEFAULT_RENDITION;
1577
void KTerminalDisplay::calcGeometry()
1579
_leftMargin = DEFAULT_LEFT_MARGIN;
1580
_contentWidth = width() - 2 * DEFAULT_LEFT_MARGIN;
1582
_topMargin = DEFAULT_TOP_MARGIN;
1583
_contentHeight = height() - 2 * DEFAULT_TOP_MARGIN + /* mysterious */ 1;
1585
// ensure that display is always at least one column wide
1586
_columns = qMax(1, qFloor(_contentWidth / (double)_fontWidth));
1587
_usedColumns = qMin(_usedColumns,_columns);
1589
// ensure that display is always at least one line high
1590
_lines = qMax(1, qFloor(_contentHeight / (double)_fontHeight));
1591
_usedLines = qMin(_usedLines,_lines);
1594
void KTerminalDisplay::makeImage()
1598
// confirm that array will be of non-zero size, since the painting code
1599
// assumes a non-zero array length
1600
Q_ASSERT( _lines > 0 && _columns > 0 );
1601
Q_ASSERT( _usedLines <= _lines && _usedColumns <= _columns );
1603
_imageSize=_lines*_columns;
1605
// We over-commit one character so that we can be more relaxed in dealing with
1606
// certain boundary conditions: _image[_imageSize] is a valid but unused position
1607
_image = new Character[_imageSize+1];
1612
uint KTerminalDisplay::lineSpacing() const
1614
return _lineSpacing;
1617
void KTerminalDisplay::setLineSpacing(uint i)
1620
setVTFont(m_font); // Trigger an update.
1625
/////////////////////////////////////////////////////////////////////////////////////
1626
/////////////////////////////////////////////////////////////////////////////////////
1628
/////////////////////////////////////////////////////////////////////////////////////
1629
/////////////////////////////////////////////////////////////////////////////////////
1631
QRect KTerminalDisplay::imageToWidget(const QRect& imageArea) const
1634
result.setLeft( _leftMargin + _fontWidth * imageArea.left() );
1635
result.setTop( _topMargin + _fontHeight * imageArea.top() );
1636
result.setWidth( _fontWidth * imageArea.width() );
1637
result.setHeight( _fontHeight * imageArea.height() );
1642
void KTerminalDisplay::paint(QPainter *painter)
1644
//contentsBoundingRect()
1645
QRectF rect = m_widgetRect;
1646
drawBackground(painter, rect, _colorTable[DEFAULT_BACK_COLOR].color, true /* use opacity setting */);
1648
/////////////////////////////////////
1650
/// THIS FUNCTION REQUIRE PATCH
1651
/////////////////////////////////////
1652
drawContents(painter, rect);
1653
/////////////////////////////////////
1654
/////////////////////////////////////
1656
drawInputMethodPreeditString(painter, preeditRect());
1657
//paintFilters(painter);
1660
void KTerminalDisplay::drawContents(QPainter *paint, QRectF &rect)
1663
int left_ = ceil(rect.left());
1664
int top_ = ceil(rect.top());
1665
int right_ = ceil(rect.right());
1666
int bottom_ = ceil(rect.bottom());
1668
int lux = qMin(_usedColumns-1, qMax(0, qRound((left_ + _leftMargin ) / _fontWidth )));
1669
int luy = qMin(_usedLines-1, qMax(0, qRound((top_ + _topMargin ) / _fontHeight )));
1670
int rlx = qMin(_usedColumns-1, qMax(0, qRound((right_ - _leftMargin ) / _fontWidth )));
1671
int rly = qMin(_usedLines-1, qMax(0, qRound((bottom_ - _topMargin ) / _fontHeight )));
1673
// prevent zero size buffer
1674
if (_usedColumns<=1) return;
1676
const int bufferSize = _usedColumns;
1678
unistr.reserve(bufferSize);
1679
for (int y = luy; y <= rly; y++)
1681
quint16 c = _image[loc(lux,y)].character;
1684
x--; // Search for start of multi-column character
1685
for (; x <= rlx; x++)
1690
// reset our buffer to the maximal size
1691
unistr.resize(bufferSize);
1692
QChar *disstrU = unistr.data();
1694
// is this a single character or a sequence of characters ?
1695
if ( _image[loc(x,y)].rendition & RE_EXTENDED_CHAR )
1697
// sequence of characters
1698
ushort extendedCharLength = 0;
1699
ushort* chars = ExtendedCharTable::instance
1700
.lookupExtendedChar(_image[loc(x,y)].charSequence,extendedCharLength);
1701
for ( int index = 0 ; index < extendedCharLength ; index++ )
1703
Q_ASSERT( p < bufferSize );
1704
disstrU[p++] = chars[index];
1710
c = _image[loc(x,y)].character;
1713
Q_ASSERT( p < bufferSize );
1714
disstrU[p++] = c; //fontMap(c);
1718
bool lineDraw = isLineChar(c);
1719
bool doubleWidth = (_image[ qMin(loc(x,y)+1,_imageSize) ].character == 0);
1720
CharacterColor currentForeground = _image[loc(x,y)].foregroundColor;
1721
CharacterColor currentBackground = _image[loc(x,y)].backgroundColor;
1722
quint8 currentRendition = _image[loc(x,y)].rendition;
1724
while (x+len <= rlx &&
1725
_image[loc(x+len,y)].foregroundColor == currentForeground &&
1726
_image[loc(x+len,y)].backgroundColor == currentBackground &&
1727
_image[loc(x+len,y)].rendition == currentRendition &&
1728
(_image[ qMin(loc(x+len,y)+1,_imageSize) ].character == 0) == doubleWidth &&
1729
isLineChar( c = _image[loc(x+len,y)].character) == lineDraw) // Assignment!
1732
disstrU[p++] = c; //fontMap(c);
1733
if (doubleWidth) // assert((_image[loc(x+len,y)+1].character == 0)), see above if condition
1734
len++; // Skip trailing part of multi-column character
1737
if ((x+len < _usedColumns) && (!_image[loc(x+len,y)].character))
1738
len++; // Adjust for trailing part of multi-column character
1740
bool save__fixedFont = _fixedFont;
1748
// Create a text scaling matrix for double width and double height lines.
1751
if (y < _lineProperties.size())
1753
if (_lineProperties[y] & LINE_DOUBLEWIDTH)
1754
textScale.scale(2,1);
1756
if (_lineProperties[y] & LINE_DOUBLEHEIGHT)
1757
textScale.scale(1,2);
1760
//Apply text scaling matrix.
1761
paint->setWorldMatrix(textScale, true);
1763
//calculate the area in which the text will be drawn
1764
QRectF textArea = QRectF( _leftMargin + _fontWidth*x , _topMargin + _fontHeight*y , _fontWidth*len , _fontHeight);
1766
//move the calculated area to take account of scaling applied to the painter.
1767
//the position of the area from the origin (0,0) is scaled
1768
//by the opposite of whatever
1769
//transformation has been applied to the painter. this ensures that
1770
//painting does actually start from textArea.topLeft()
1771
//(instead of textArea.topLeft() * painter-scale)
1772
textArea.moveTopLeft( textScale.inverted().map(textArea.topLeft()) );
1774
//paint text fragment
1775
drawTextFragment( paint,
1778
&_image[loc(x,y)] ); //,
1782
_fixedFont = save__fixedFont;
1784
//reset back to single-width, single-height _lines
1785
paint->setWorldMatrix(textScale.inverted(), true);
1787
if (y < _lineProperties.size()-1)
1789
//double-height _lines are represented by two adjacent _lines
1790
//containing the same characters
1791
//both _lines will have the LINE_DOUBLEHEIGHT attribute.
1792
//If the current line has the LINE_DOUBLEHEIGHT attribute,
1793
//we can therefore skip the next line
1794
if (_lineProperties[y] & LINE_DOUBLEHEIGHT)
1804
void KTerminalDisplay::drawLineCharString( QPainter* painter, qreal x, qreal y, const QString& str,
1805
const Character* attributes)
1807
const QPen& currentPen = painter->pen();
1809
if ( (attributes->rendition & RE_BOLD) && _boldIntense )
1811
QPen boldPen(currentPen);
1812
boldPen.setWidth(3);
1813
painter->setPen( boldPen );
1816
for (int i=0 ; i < str.length(); i++)
1818
uchar code = str[i].cell();
1819
if (LineChars[code])
1820
drawLineChar(painter, x + (_fontWidth*i), y, _fontWidth, _fontHeight, code);
1823
painter->setPen( currentPen );
1826
void KTerminalDisplay::drawBackground(QPainter* painter, const QRectF &rect, const QColor& backgroundColor, bool useOpacitySetting )
1828
// the area of the widget showing the contents of the terminal display is drawn
1829
// using the background color from the color scheme set with setColorTable()
1831
QRectF contentsRect = rect;
1833
if ( HAVE_TRANSPARENCY && qAlpha(_blendColor) < 0xff && useOpacitySetting )
1835
QColor color(backgroundColor);
1836
color.setAlpha(qAlpha(_blendColor));
1839
painter->setCompositionMode(QPainter::CompositionMode_Source);
1840
painter->fillRect(contentsRect, color);
1844
painter->fillRect(contentsRect, backgroundColor);
1848
void KTerminalDisplay::drawCursor(QPainter* painter,
1850
const QColor& foregroundColor,
1851
const QColor& /*backgroundColor*/,
1852
bool& invertCharacterColor)
1854
QRectF cursorRect = rect;
1855
cursorRect.setHeight(_fontHeight - _lineSpacing - 1);
1857
if (!_cursorBlinking)
1859
if ( _cursorColor.isValid() )
1860
painter->setPen(_cursorColor);
1862
painter->setPen(foregroundColor);
1864
if ( _cursorShape == BlockCursor )
1866
// draw the cursor outline, adjusting the area so that
1867
// it is draw entirely inside 'rect'
1868
int penWidth = qMax(1,painter->pen().width());
1870
painter->drawRect(cursorRect.adjusted( penWidth/2,
1872
- penWidth/2 - penWidth%2,
1873
- penWidth/2 - penWidth%2));
1876
painter->fillRect(cursorRect, _cursorColor.isValid() ? _cursorColor : foregroundColor);
1878
if ( !_cursorColor.isValid() )
1880
// invert the colour used to draw the text to ensure that the character at
1881
// the cursor position is readable
1882
invertCharacterColor = true;
1886
else if ( _cursorShape == UnderlineCursor )
1887
painter->drawLine(cursorRect.left(),
1888
cursorRect.bottom(),
1890
cursorRect.bottom());
1891
else if ( _cursorShape == IBeamCursor )
1892
painter->drawLine(cursorRect.left(),
1895
cursorRect.bottom());
1900
void KTerminalDisplay::drawCharacters(QPainter* painter,
1902
const QString& text,
1903
const Character* style,
1904
bool invertCharacterColor)
1906
// don't draw text which is currently blinking
1907
if ( _blinking && (style->rendition & RE_BLINK) )
1910
// setup bold and underline
1912
ColorEntry::FontWeight weight = style->fontWeight(_colorTable);
1913
if (weight == ColorEntry::UseCurrentFormat)
1914
useBold = ((style->rendition & RE_BOLD) && _boldIntense) || m_font.bold();
1916
useBold = (weight == ColorEntry::Bold) ? true : false;
1917
bool useUnderline = style->rendition & RE_UNDERLINE || m_font.underline();
1919
QFont font = m_font;
1920
QFont font_ = painter->font();
1921
if ( font.bold() != useBold
1922
|| font.underline() != useUnderline )
1924
font.setBold(useBold);
1925
font.setUnderline(useUnderline);
1929
#if QT_VERSION >= 0x040700
1930
font.setStyleStrategy(QFont::ForceIntegerMetrics);
1932
#warning "Correct handling of the QFont metrics requited Qt>=4.7"
1936
painter->setFont(font);
1939
const CharacterColor& textColor = ( invertCharacterColor ? style->backgroundColor : style->foregroundColor );
1940
const QColor color = textColor.color(_colorTable);
1941
QPen pen = painter->pen();
1942
if ( pen.color() != color )
1944
pen.setColor(color);
1945
painter->setPen(color);
1949
if ( isLineCharString(text) )
1950
drawLineCharString(painter,rect.x(),rect.y(),text,style);
1954
painter->drawText(rect,text);
1956
painter->drawText(rect,LTR_OVERRIDE_CHAR+text);
1959
painter->setFont(font_);
1962
void KTerminalDisplay::drawTextFragment(QPainter* painter ,
1964
const QString& text,
1965
const Character* style)
1970
const QColor foregroundColor = style->foregroundColor.color(_colorTable);
1971
const QColor backgroundColor = style->backgroundColor.color(_colorTable);
1973
// draw background if different from the display's background color
1974
if ( backgroundColor != m_palette.background().color() )
1975
drawBackground(painter, rect, backgroundColor, false /* do not use transparency */);
1977
// draw cursor shape if the current character is the cursor
1978
// this may alter the foreground and background colors
1979
bool invertCharacterColor = false;
1980
if ( style->rendition & RE_CURSOR )
1981
drawCursor(painter, rect, foregroundColor,backgroundColor,invertCharacterColor);
1984
drawCharacters(painter, rect, text, style, invertCharacterColor);
1989
void KTerminalDisplay::getCharacterPosition(const QPointF &widgetPoint, int& line, int& column) const
1991
//contentsBoundingRect()
1992
QRectF rect = m_widgetRect;
1994
column = qRound((widgetPoint.x() + _fontWidth/2 - rect.left()-_leftMargin) / _fontWidth);
1995
line = qRound((widgetPoint.y() - rect.top()-_topMargin) / _fontHeight);
2002
if ( line >= _usedLines )
2003
line = _usedLines-1;
2005
// the column value returned can be equal to _usedColumns, which
2006
// is the position just after the last character displayed in a line.
2008
// this is required so that the user can select characters in the right-most
2009
// column (or left-most for right-to-left input)
2010
if ( column > _usedColumns )
2011
column = _usedColumns;
2014
QRectF KTerminalDisplay::preeditRect() const
2016
const int preeditLength = string_width(_inputMethodData.preeditString);
2018
if ( preeditLength == 0 )
2021
return QRectF(_leftMargin + _fontWidth*cursorPosition().x(),
2022
_topMargin + _fontHeight*cursorPosition().y(),
2023
_fontWidth*preeditLength,
2027
void KTerminalDisplay::drawInputMethodPreeditString(QPainter *painter , const QRectF &rect)
2029
if ( _inputMethodData.preeditString.isEmpty() )
2032
const QPoint cursorPos = cursorPosition();
2034
bool invertColors = false;
2035
const QColor background = _colorTable[DEFAULT_BACK_COLOR].color;
2036
const QColor foreground = _colorTable[DEFAULT_FORE_COLOR].color;
2037
const Character* style = &_image[loc(cursorPos.x(),cursorPos.y())];
2039
drawBackground(painter, rect, background,true);
2040
drawCursor(painter, rect, foreground,background,invertColors);
2041
drawCharacters(painter, rect,_inputMethodData.preeditString,style,invertColors);
2043
_inputMethodData.previousPreeditRect = rect;
2048
void KTerminalDisplay::keyPressEvent(QKeyEvent *event)
2050
bool emitKeyPressSignal = true;
2052
// Keyboard-based navigation
2053
if ( event->modifiers() == Qt::ShiftModifier )
2057
if ( event->key() == Qt::Key_PageUp )
2059
_screenWindow->scrollBy( ScreenWindow::ScrollPages , -1 );
2061
else if ( event->key() == Qt::Key_PageDown )
2063
_screenWindow->scrollBy( ScreenWindow::ScrollPages , 1 );
2065
else if ( event->key() == Qt::Key_Up )
2067
_screenWindow->scrollBy( ScreenWindow::ScrollLines , -1 );
2069
else if ( event->key() == Qt::Key_Down )
2071
_screenWindow->scrollBy( ScreenWindow::ScrollLines , 1 );
2078
_screenWindow->setTrackOutput( _screenWindow->atEndOfOutput() );
2080
updateLineProperties();
2083
// do not send key press to terminal
2084
emitKeyPressSignal = false;
2088
_actSel=0; // Key stroke implies a screen update, so TerminalDisplay won't
2089
// know where the current selection is.
2091
if (_hasBlinkingCursor)
2093
_blinkCursorTimer->start(100); //WARN! HARDCODE
2094
if (_cursorBlinking)
2097
_cursorBlinking = false;
2100
if ( emitKeyPressSignal )
2101
emit keyPressedSignal(event);
2107
/////////////////////////////////////////////////////////////////////////////////////
2108
/////////////////////////////////////////////////////////////////////////////////////
2110
/////////////////////////////////////////////////////////////////////////////////////
2111
/////////////////////////////////////////////////////////////////////////////////////
2113
AutoScrollHandler::AutoScrollHandler(QQuickItem* parent)
2117
parent->installEventFilter(this);
2119
void AutoScrollHandler::timerEvent(QTimerEvent* event)
2121
if (event->timerId() != _timerId)
2124
QMouseEvent mouseEvent( QEvent::MouseMove,
2125
widget()->mapFromScene(QCursor::pos()),
2130
QGuiApplication::sendEvent(widget(),&mouseEvent);
2132
bool AutoScrollHandler::eventFilter(QObject* watched,QEvent* event)
2134
Q_ASSERT( watched == parent() );
2135
Q_UNUSED( watched );
2137
QMouseEvent* mouseEvent = (QMouseEvent*)event;
2138
switch (event->type())
2140
case QEvent::MouseMove:
2142
bool mouseInWidget = false; //widget()->rect().contains(mouseEvent->pos());
2147
killTimer(_timerId);
2152
if (!_timerId && (mouseEvent->buttons() & Qt::LeftButton))
2153
_timerId = startTimer(100);
2157
case QEvent::MouseButtonRelease:
2158
if (_timerId && (mouseEvent->buttons() & ~Qt::LeftButton))
2160
killTimer(_timerId);
2171
//#include "TerminalDisplay.moc"