/*************************************************************************** * Copyright (C) 2008-2012 by Heiko Koehn - KoehnHeiko@googlemail.com * * Copyright (C) 2014 by Ahmed Charles - acharles@outlook.com * * Copyright (C) 2014-2016 by Stephen Lyons - slysven@virginmedia.com * * Copyright (C) 2016-2017 by Ian Adkins - ieadkins@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "TTextEdit.h" #include "Host.h" #include "TConsole.h" #include "TEvent.h" #include "pre_guard.h" #include #include #include #include #include #include #include #include #include "post_guard.h" TTextEdit::TTextEdit( TConsole * pC, QWidget * pW, TBuffer * pB, Host * pH, bool isDebugConsole, bool isSplitScreen ) : QWidget( pW ) , mCursorY( 0 ) , mIsCommandPopup( false ) , mIsTailMode( true ) , mShowTimeStamps( isDebugConsole ) , mForceUpdate( false ) , mHighlight_on( false ) , mHighlightingBegin( false ) , mHighlightingEnd( false ) , mInit_OK( false ) , mInversOn( false ) , mIsDebugConsole( isDebugConsole ) , mIsMiniConsole( false ) , mIsSplitScreen( isSplitScreen ) , mLastRenderBottom( 0 ) , mMouseTracking( false ) , mPainterInit( false ) , mpBuffer( pB ) , mpConsole( pC ) , mpHost( pH ) , mpScrollBar( 0 ) { mLastClickTimer.start(); if( ! mIsDebugConsole ) { mFontHeight = QFontMetrics( mpHost->mDisplayFont ).height(); mFontWidth = QFontMetrics( mpHost->mDisplayFont ).width( QChar('W') ); mScreenWidth = 100; if( (width()/mFontWidth ) < mScreenWidth ) { mScreenWidth = 100;//width()/mFontWidth; } mpHost->mDisplayFont.setFixedPitch(true); #if defined(Q_OS_MAC) || defined(Q_OS_LINUX) QPixmap pixmap = QPixmap( mScreenWidth*mFontWidth*2, mFontHeight*2 ); QPainter p(&pixmap); p.setFont(mpHost->mDisplayFont); const QRectF r = QRectF(0,0,mScreenWidth*mFontWidth*2,mFontHeight*2); QRectF r2; const QString t = "1234"; p.drawText(r,1,t,&r2); mLetterSpacing = (qreal)((qreal)mFontWidth-(qreal)(r2.width()/t.size())); mpHost->mDisplayFont.setLetterSpacing( QFont::AbsoluteSpacing, mLetterSpacing ); #endif setFont( mpHost->mDisplayFont ); } else { mIsDebugConsole = true; mFontHeight = QFontMetrics( mDisplayFont ).height(); mFontWidth = QFontMetrics( mDisplayFont ).width( QChar('W') ); mScreenWidth = 100; mDisplayFont.setFixedPitch(true); #if defined(Q_OS_MAC) || defined(Q_OS_LINUX) QPixmap pixmap = QPixmap( mScreenWidth*mFontWidth*2, mFontHeight*2 ); QPainter p(&pixmap); p.setFont(mDisplayFont); const QRectF r = QRectF(0,0,mScreenWidth*mFontWidth*2,mFontHeight*2); QRectF r2; const QString t = "1234"; p.drawText(r,1,t,&r2); mLetterSpacing = (qreal)((qreal)mFontWidth-(qreal)(r2.width()/t.size())); mDisplayFont.setLetterSpacing( QFont::AbsoluteSpacing, mLetterSpacing ); #endif setFont( mDisplayFont ); // initialize after mFontHeight and mFontWidth have been set, because the function uses them! initDefaultSettings(); } mScreenHeight = height() / mFontHeight; mScreenWidth = 100; setMouseTracking( true ); setFocusPolicy( Qt::NoFocus ); QCursor cursor; cursor.setShape(Qt::IBeamCursor); setCursor( cursor ); setAttribute( Qt::WA_OpaquePaintEvent );//was disabled setAttribute( Qt::WA_DeleteOnClose ); QPalette palette; palette.setColor( QPalette::Text, mFgColor ); palette.setColor( QPalette::Highlight, QColor(55,55,255) ); palette.setColor( QPalette::Base, mBgColor ); setPalette(palette); showNewLines(); setMouseTracking( true ); // test fix for MAC setEnabled( true ); //test fix for MAC } void TTextEdit::forceUpdate() { mForceUpdate = true; update(); } void TTextEdit::needUpdate( int y1, int y2 ) { if( ! mIsTailMode ) { return; } if( mScreenHeight == 0 ) return; int top = imageTopLine(); int bottom = y2-y1; if( top > 0 ) { top = (y1 - top) % mScreenHeight; } else { top = y1 % mScreenHeight; } QRect r( 0, top*mFontHeight, mScreenWidth*mFontWidth, bottom*mFontHeight ); mForceUpdate = true; update( r ); } void TTextEdit::focusInEvent ( QFocusEvent * event ) { update(); QWidget::focusInEvent( event ); } void TTextEdit::slot_toggleTimeStamps() { mShowTimeStamps = !mShowTimeStamps; forceUpdate(); update(); } void TTextEdit::slot_scrollBarMoved( int line ) { if( mpConsole->mpScrollBar ) { disconnect( mpConsole->mpScrollBar, SIGNAL(valueChanged(int)), this, SLOT(slot_scrollBarMoved(int))); mpConsole->mpScrollBar->setRange( 0, mpBuffer->getLastLineNumber() ); mpConsole->mpScrollBar->setSingleStep( 1 ); mpConsole->mpScrollBar->setPageStep( mScreenHeight ); mpConsole->mpScrollBar->setValue( line ); scrollTo( line ); connect( mpConsole->mpScrollBar, SIGNAL(valueChanged(int)), this, SLOT(slot_scrollBarMoved(int))); } } void TTextEdit::initDefaultSettings() { mFgColor = QColor(192,192,192); mBgColor = QColor(Qt::black); mDisplayFont = QFont("Bitstream Vera Sans Mono", 10, QFont::Normal); #if defined(Q_OS_MAC) || defined(Q_OS_LINUX) int width = mScreenWidth*mFontWidth*2; int height = mFontHeight*2; // sometimes mScreenWidth is 0, and QPainter doesn't like dimensions of 0x#. Need to work out why is // mScreenWidth ever zero and it gets used in the follow calculations. if ( width > 0 && height > 0 ) { QPixmap pixmap = QPixmap( width, height ); QPainter p(&pixmap); p.setFont(mDisplayFont); const QRectF r = QRectF( 0,0,width,height ); QRectF r2; const QString t = "1234"; p.drawText(r,1,t,&r2); mLetterSpacing = (qreal)((qreal)mFontWidth-(qreal)(r2.width()/t.size())); mDisplayFont.setLetterSpacing( QFont::AbsoluteSpacing, mLetterSpacing ); } #endif mDisplayFont.setLetterSpacing( QFont::AbsoluteSpacing, mLetterSpacing ); mDisplayFont.setFixedPitch(true); setFont( mDisplayFont ); mCommandLineFont = QFont("Bitstream Vera Sans Mono", 10, QFont::Normal); mCommandSeperator = QString(";"); mWrapAt = 100; mWrapIndentCount = 5; } void TTextEdit::updateScreenView() { if( isHidden() ) { mFontWidth = QFontMetrics( mDisplayFont ).width( QChar(' ') ); mFontDescent = QFontMetrics( mDisplayFont ).descent(); mFontAscent = QFontMetrics( mDisplayFont ).ascent(); mFontHeight = mFontAscent + mFontDescent; #if defined(Q_OS_MAC) || defined(Q_OS_LINUX) QPixmap pixmap = QPixmap( 2000,600 ); QPainter p(&pixmap); mDisplayFont.setLetterSpacing(QFont::AbsoluteSpacing, 0); if( ! p.isActive() ) return; p.setFont(mDisplayFont); const QRectF r = QRectF(0,0,2000,600); QRectF r2; const QString t = "1234"; p.drawText(r,1,t,&r2); mLetterSpacing = (qreal)((qreal)mFontWidth-(qreal)(r2.width()/t.size())); mDisplayFont.setLetterSpacing( QFont::AbsoluteSpacing, mLetterSpacing ); #endif return; //NOTE: das ist wichtig, damit ich keine floating point exception bekomme, wenn mScreenHeight==0, was hier der Fall wäre } if( ! mIsDebugConsole && ! mIsMiniConsole ) { mFontWidth = QFontMetrics( mpHost->mDisplayFont ).width( QChar('W') ); mFontDescent = QFontMetrics( mpHost->mDisplayFont ).descent(); mFontAscent = QFontMetrics( mpHost->mDisplayFont ).ascent(); mFontHeight = mFontAscent + mFontDescent; mBgColor = mpHost->mBgColor; mFgColor = mpHost->mFgColor; #if defined(Q_OS_MAC) || defined(Q_OS_LINUX) QPixmap pixmap = QPixmap( mScreenWidth*mFontWidth*2, mFontHeight*2 ); QPainter p(&pixmap); mpHost->mDisplayFont.setLetterSpacing(QFont::AbsoluteSpacing, 0); if( p.isActive() ) { p.setFont(mpHost->mDisplayFont); const QRectF r = QRectF(0,0,mScreenWidth*mFontWidth*2,mFontHeight*2); QRectF r2; const QString t = "1234"; p.drawText(r,1,t,&r2); mLetterSpacing = (qreal)((qreal)mFontWidth-(qreal)(r2.width()/t.size())); mpHost->mDisplayFont.setLetterSpacing( QFont::AbsoluteSpacing, mLetterSpacing ); } #endif } else { mFontWidth = QFontMetrics( mDisplayFont ).width( QChar('W') ); mFontDescent = QFontMetrics( mDisplayFont ).descent(); mFontAscent = QFontMetrics( mDisplayFont ).ascent(); mFontHeight = mFontAscent + mFontDescent; #if defined(Q_OS_MAC) || defined(Q_OS_LINUX) int width = mScreenWidth*mFontWidth*2; int height = mFontHeight*2; // sometimes mScreenWidth is 0, and QPainter doesn't like dimensions of 0x#. Need to work out why is // mScreenWidth ever zero and it gets used in the follow calculations. if ( width > 0 && height > 0 ) { QPixmap pixmap = QPixmap( width, height ); QPainter p(&pixmap); mDisplayFont.setLetterSpacing(QFont::AbsoluteSpacing, 0); if( p.isActive() ) { p.setFont(mDisplayFont); const QRectF r = QRectF( 0,0,width,height ); QRectF r2; const QString t = "1234"; p.drawText(r,1,t,&r2); mLetterSpacing = (qreal)((qreal)mFontWidth-(qreal)(r2.width()/t.size())); mDisplayFont.setLetterSpacing( QFont::AbsoluteSpacing, mLetterSpacing ); } } #endif } mScreenHeight = visibleRegion().boundingRect().height()/mFontHeight; int currentScreenWidth = visibleRegion().boundingRect().width() / mFontWidth; if( ! mIsDebugConsole && ! mIsMiniConsole ) { if( mpHost->mScreenWidth > currentScreenWidth ) { if( currentScreenWidth < 100 ) { mScreenWidth = 100; } else { mScreenWidth = currentScreenWidth; mpHost->mScreenWidth = mScreenWidth; } } else { mpHost->mScreenWidth = currentScreenWidth; mScreenWidth = currentScreenWidth; } mpHost->mScreenHeight = mScreenHeight; } else { mScreenWidth = currentScreenWidth; } } void TTextEdit::showNewLines() { if( ! mIsSplitScreen ) { if( ! isTailMode() ) { return; } } else { if( isHidden() ) { return; } } mCursorY = mpBuffer->size(); if( ! mIsSplitScreen ) { mpBuffer->mCursorY = mpBuffer->size(); } if( mCursorY > mScreenHeight ) { mScrollUp = true; } mOldScrollPos = mpBuffer->getLastLineNumber(); if( ! mIsSplitScreen ) { if( mpConsole->mpScrollBar && mOldScrollPos > 0 ) { disconnect( mpConsole->mpScrollBar, SIGNAL(valueChanged(int)), mpConsole->console, SLOT(slot_scrollBarMoved(int))); mpConsole->mpScrollBar->setRange( 0, mpBuffer->getLastLineNumber() ); mpConsole->mpScrollBar->setSingleStep( 1 ); mpConsole->mpScrollBar->setPageStep( mScreenHeight ); if( mpConsole->console->isTailMode() ) { mpConsole->mpScrollBar->setValue( mpBuffer->mCursorY ); } connect( mpConsole->mpScrollBar, SIGNAL(valueChanged(int)), mpConsole->console, SLOT(slot_scrollBarMoved(int))); } } update(); } void TTextEdit::scrollTo( int line ) { if( (line > -1) && (line < mpBuffer->size()) ) { if( (line < (mpBuffer->getLastLineNumber()-mScreenHeight) && mIsTailMode ) ) { mpConsole->console2->mCursorY = mpBuffer->size(); mpConsole->console2->mIsTailMode = true; mIsTailMode = false; mpConsole->console2->show(); mpConsole->console2->forceUpdate(); } else if( (line > (mpBuffer->getLastLineNumber()-mScreenHeight)) && !mIsTailMode ) { mpConsole->console2->mCursorY = mpConsole->buffer.getLastLineNumber(); mpConsole->console2->hide(); mpConsole->console->mCursorY = mpConsole->buffer.getLastLineNumber(); mpConsole->console->mIsTailMode = true; mpConsole->console->updateScreenView(); mpConsole->console->forceUpdate(); } mpBuffer->mCursorY = line; mScrollVector = 0; update(); } } void TTextEdit::scrollUp( int lines ) { if( mIsSplitScreen ) return; lines = bufferScrollUp( lines ); if( lines == 0 ) return; else { mIsTailMode = false; lines = mScreenHeight; mScrollVector = 0; update(); } } void TTextEdit::scrollDown( int lines ) { if( mIsSplitScreen ) return; lines = bufferScrollDown( lines ); if( lines == 0 ) return; else { mScrollVector = 0; update(); } } inline void TTextEdit::drawBackground( QPainter & painter, const QRect & rect, const QColor & bgColor ) { QRect bR = rect; painter.fillRect( bR.x(), bR.y(), bR.width(), bR.height(), bgColor ); } inline void TTextEdit::drawCharacters( QPainter & painter, const QRect & rect, QString & text, bool isBold, bool isUnderline, bool isItalics, bool isStrikeOut, QColor & fgColor, QColor & bgColor ) { if( ( painter.font().bold() != isBold ) || ( painter.font().underline() != isUnderline ) || ( painter.font().italic() != isItalics ) || ( painter.font().strikeOut() != isStrikeOut) ) { QFont font = painter.font(); font.setBold( isBold ); font.setUnderline( isUnderline ); font.setItalic( isItalics ); font.setStrikeOut( isStrikeOut ); #if defined(Q_OS_MAC) || defined(Q_OS_LINUX) font.setLetterSpacing(QFont::AbsoluteSpacing, mLetterSpacing); #endif painter.setFont( font ); } if( painter.pen().color() != fgColor ) { painter.setPen( fgColor ); } #if defined(Q_OS_MAC) || defined(Q_OS_LINUX) QPointF _p(rect.x(), rect.bottom()-mFontDescent); painter.drawText( _p, text ); #else painter.drawText( rect.x(), rect.bottom()-mFontDescent, text ); #endif } void TTextEdit::drawFrame( QPainter & p, const QRect & rect ) { QPoint P_topLeft = rect.topLeft(); QPoint P_bottomRight = rect.bottomRight(); int x_topLeft = P_topLeft.x(); int x_bottomRight = P_bottomRight.x(); if( x_bottomRight > mScreenWidth * mFontWidth ) x_bottomRight = mScreenWidth * mFontWidth; int x1 = x_topLeft / mFontWidth; int x2 = x_bottomRight / mFontWidth; int lineOffset = imageTopLine(); bool invers = false; if( mHighlight_on && mInversOn ) { invers = true; } int from = 0; for( int i=from; i(mpBuffer->buffer.size()) <= i+lineOffset ) { break; } int timeOffset = 0; if( mShowTimeStamps ) { if( mpBuffer->timeBuffer.size() > i+lineOffset ) { timeOffset = mpBuffer->timeBuffer[i+lineOffset].size()-1; } } int lineLength = mpBuffer->buffer[i+lineOffset].size() + timeOffset; for( int i2=x1; i2< timeOffset ) { text = mpBuffer->timeBuffer[i+lineOffset]; bool isBold = false; bool isUnderline = false; bool isItalics = false; bool isStrikeOut = false; QRect textRect = QRect( mFontWidth * i2, mFontHeight * i, mFontWidth * timeOffset, mFontHeight ); auto bgTime = QColor(22,22,22); auto fgTime = QColor(200,150,0); drawBackground( p, textRect, bgTime ); drawCharacters( p, textRect, text, isBold, isUnderline, isItalics, isStrikeOut, fgTime, bgTime ); i2+=timeOffset; } else { if( i2 >= x2 ) { break; } text = mpBuffer->lineBuffer[i+lineOffset].at(i2-timeOffset); TChar & f = mpBuffer->buffer[i+lineOffset][i2-timeOffset]; int delta = 1; auto fgColor = QColor(f.fgR, f.fgG, f.fgB ); auto bgColor = QColor(f.bgR, f.bgG, f.bgB ); while( i2+delta+timeOffset < lineLength ) { if( mpBuffer->buffer[i+lineOffset][i2+delta-timeOffset] == f ) { text.append( mpBuffer->lineBuffer[i+lineOffset].at(i2+delta-timeOffset) ); delta++; } else { break; } } if( invers || ( bgColor != mBgColor ) ) { QRect textRect = QRect( mFontWidth * i2, mFontHeight * i, mFontWidth * delta, mFontHeight ); if( invers ) drawBackground( p, textRect, fgColor ); else drawBackground( p, textRect, bgColor ); } if( ( p.font().bold() != static_cast(f.flags & TCHAR_BOLD) ) || ( p.font().underline() != static_cast(f.flags & TCHAR_UNDERLINE) ) || ( p.font().italic() != static_cast(f.flags & TCHAR_ITALICS) ) || ( p.font().strikeOut() != static_cast(f.flags & TCHAR_STRIKEOUT) ) ) { QFont font = p.font(); font.setBold( f.flags & TCHAR_BOLD ); font.setUnderline( f.flags & TCHAR_UNDERLINE ); font.setItalic( f.flags & TCHAR_ITALICS ); font.setStrikeOut( f.flags & TCHAR_STRIKEOUT ); font.setLetterSpacing( QFont::AbsoluteSpacing, mLetterSpacing ); p.setFont( font ); } if( ( p.pen().color() != fgColor ) || ( invers ) ) { if( invers ) p.setPen( bgColor ); else p.setPen( fgColor ); } p.drawText( mFontWidth*i2,(mFontHeight*i)-mFontDescent, text ); i2+=delta; } } } mScrollVector = 0; } void TTextEdit::updateLastLine() { qDebug()<<"--->ACHTUNG: error: updateLastLine() called"; QRect r( 0, (mScreenHeight-1)*mFontHeight, mScreenWidth*mFontWidth, mScreenHeight*mFontHeight ); mForceUpdate = true; update( r ); } void TTextEdit::drawForeground( QPainter & painter, const QRect & r ) { QPixmap screenPixmap; QPixmap pixmap = QPixmap( mScreenWidth*mFontWidth, mScreenHeight*mFontHeight ); pixmap.fill( palette().base().color() ); QPainter p( &pixmap ); p.setCompositionMode( QPainter::CompositionMode_Source ); if( ! mIsDebugConsole && ! mIsMiniConsole ) { p.setFont( mpHost->mDisplayFont ); p.setRenderHint( QPainter::TextAntialiasing, !mpHost->mNoAntiAlias ); } else { p.setFont( mDisplayFont ); p.setRenderHint( QPainter::TextAntialiasing, false ); } QPoint P_topLeft = r.topLeft(); QPoint P_bottomRight = r.bottomRight(); int x_topLeft = 0; int y_topLeft = P_topLeft.y(); int x_bottomRight = P_bottomRight.x(); int y_bottomRight = P_bottomRight.y(); if( x_bottomRight > mScreenWidth * mFontWidth ) { x_bottomRight = mScreenWidth * mFontWidth; } int x1 = x_topLeft / mFontWidth; int y1 = y_topLeft / mFontHeight; int x2 = x_bottomRight / mFontWidth; int y2 = y_bottomRight / mFontHeight; int lineOffset = imageTopLine(); int from = 0; if( lineOffset == 0 ) { mScrollVector = 0; } else { mScrollVector = lineOffset - mLastRenderBottom; } bool noScroll = false; bool noCopy = false; if( abs( mScrollVector ) > mScreenHeight || mForceUpdate || lineOffset < 10 ) { mScrollVector = 0; noScroll = true; } if( ( r.height() < rect().height() ) && ( lineOffset > 0 ) ) { p.drawPixmap( 0, 0, mScreenMap ); if( ! mForceUpdate && ! mMouseTracking ) { from = y1; noScroll = true; noCopy = true; } else { from = y1; y2 = mScreenHeight; noScroll = true; mScrollVector = 0; } } if( ( ! noScroll ) && ( mScrollVector >= 0 ) && ( mScrollVector <= mScreenHeight ) && ( ! mForceUpdate ) ) { if( mScrollVector*mFontHeight < mScreenMap.height() && mScreenWidth*mFontWidth <= mScreenMap.width() && (mScreenHeight-mScrollVector)*mFontHeight > 0 && (mScreenHeight-mScrollVector)*mFontHeight <= mScreenMap.height() ) { screenPixmap = mScreenMap.copy( 0, mScrollVector*mFontHeight, mScreenWidth*mFontWidth, (mScreenHeight-mScrollVector)*mFontHeight ); p.drawPixmap( 0, 0, screenPixmap ); from = mScreenHeight - mScrollVector - 1; } } else if( ( ! noScroll ) && ( mScrollVector < 0 && mScrollVector >= ((-1)*mScreenHeight) ) && ( ! mForceUpdate ) ) { if( abs(mScrollVector)*mFontHeight < mScreenMap.height() && mScreenWidth*mFontWidth <= mScreenMap.width() && (mScreenHeight-abs(mScrollVector))*mFontHeight > 0 && (mScreenHeight-abs(mScrollVector))*mFontHeight <= mScreenMap.height() ) { screenPixmap = mScreenMap.copy( 0, 0, mScreenWidth*mFontWidth, (mScreenHeight-abs(mScrollVector))*mFontHeight ); p.drawPixmap( 0, abs(mScrollVector)*mFontHeight, screenPixmap ); from = 0; y2 = abs(mScrollVector); } } QRect deleteRect = QRect( 0, from*mFontHeight, x2*mFontHeight, (y2+1)*mFontHeight); drawBackground( p, deleteRect, mBgColor ); for( int i=from; i<=y2; i++ ) { if( static_cast(mpBuffer->buffer.size()) <= i+lineOffset ) { break; } mpBuffer->dirty[lineOffset+i] = false; int timeOffset = 0; if( mShowTimeStamps ) { timeOffset = 13; } int lineLength = mpBuffer->buffer[i+lineOffset].size() + timeOffset; for( int i2=x1; i2< timeOffset ) { text = mpBuffer->timeBuffer[i+lineOffset]; bool isBold = false; bool isUnderline = false; bool isItalics = false; bool isStrikeOut = false; QRect textRect = QRect( mFontWidth * i2, mFontHeight * i, mFontWidth * timeOffset, mFontHeight ); auto bgTime = QColor(22,22,22); auto fgTime = QColor(200,150,0); drawBackground( p, textRect, bgTime ); drawCharacters( p, textRect, text, isBold, isUnderline, isItalics, isStrikeOut, fgTime, bgTime ); i2+=timeOffset; } else { if( i2 >= x2 ) { break; } text = mpBuffer->lineBuffer[i+lineOffset].at(i2-timeOffset); TChar & f = mpBuffer->buffer[i+lineOffset][i2-timeOffset]; int delta = 1; QColor fgColor; QColor bgColor; if( f.flags & TCHAR_INVERSE ) { bgColor = QColor(f.fgR, f.fgG, f.fgB ); fgColor = QColor(f.bgR, f.bgG, f.bgB ); } else { fgColor = QColor(f.fgR, f.fgG, f.fgB ); bgColor = QColor(f.bgR, f.bgG, f.bgB ); } while( i2+delta+timeOffset < lineLength ) { if( mpBuffer->buffer[i+lineOffset][i2+delta-timeOffset] == f ) { text.append( mpBuffer->lineBuffer[i+lineOffset].at(i2+delta-timeOffset) ); delta++; } else { break; } } QRect textRect; textRect = QRect( mFontWidth * i2, mFontHeight * i, mFontWidth * delta, mFontHeight ); if( f.flags & TCHAR_INVERSE || ( bgColor != mBgColor ) ) { drawBackground( p, textRect, bgColor ); } drawCharacters( p, textRect, text, f.flags & TCHAR_BOLD, f.flags & TCHAR_UNDERLINE, f.flags & TCHAR_ITALICS, f.flags & TCHAR_STRIKEOUT, fgColor, bgColor ); i2+=delta; } } } p.end(); painter.setCompositionMode( QPainter::CompositionMode_Source ); painter.drawPixmap( 0, 0, pixmap ); if( ! noCopy ) { mScreenMap = pixmap.copy(); } mScrollVector = 0; mLastRenderBottom = lineOffset; mForceUpdate = false; } void TTextEdit::paintEvent( QPaintEvent* e ) { const QRect & rect = e->rect(); if( mFontWidth <= 0 || mFontHeight <= 0 ) return; if( mScreenHeight <= 0 || mScreenWidth <= 0 ) { mScreenHeight = height()/mFontHeight; mScreenWidth = 100; if( mScreenHeight <= 0 || mScreenWidth <= 0 ) return; } QPainter painter( this ); if( ! painter.isActive() ) return; QRect borderRect = QRect( 0, mScreenHeight*mFontHeight, rect.width(), rect.height() ); drawBackground( painter, borderRect, mBgColor ); QRect borderRect2 = QRect( rect.width()-mScreenWidth, 0, rect.width(), rect.height() ); drawBackground( painter, borderRect2, mBgColor ); drawForeground( painter, rect ); mUpdateSlice = false; } void TTextEdit::highlight() { QRegion newRegion; int lineDelta = abs( mPA.y() - mPB.y() ) - 1; if( lineDelta > 0 ) { QRect rectFirstLine( mPA.x()*mFontWidth, (mPA.y()-imageTopLine())*mFontHeight, mScreenWidth*mFontWidth, mFontHeight ); newRegion += rectFirstLine; QRect rectMiddlePart( 0, (mPA.y()+1-imageTopLine())*mFontHeight, mScreenWidth*mFontWidth, lineDelta*mFontHeight ); newRegion += rectMiddlePart; QRect rectLastLine( 0, (mPB.y()-imageTopLine())*mFontHeight, mPB.x()*mFontWidth, mFontHeight ); newRegion += rectLastLine; } if( lineDelta == 0 ) { QRect rectFirstLine( mPA.x()*mFontWidth, (mPA.y()-imageTopLine())*mFontHeight, mScreenWidth*mFontWidth, mFontHeight ); newRegion += rectFirstLine; QRect rectLastLine( 0, (mPB.y()-imageTopLine())*mFontHeight, mPB.x()*mFontWidth, mFontHeight ); newRegion += rectLastLine; } if( lineDelta < 0 ) { QRect rectFirstLine( mPA.x()*mFontWidth, (mPA.y()-imageTopLine())*mFontHeight, (mPB.x()-mPA.x())*mFontWidth, mFontHeight ); newRegion += rectFirstLine; } QRect _r = mSelectedRegion.boundingRect(); if( lineDelta < 0 ) { _r.setWidth( mScreenWidth*mFontWidth ); } update( _r ); mSelectedRegion = mSelectedRegion.subtracted( newRegion ); int y1 = mPA.y(); for( int y=y1; y<=mPB.y(); y++ ) { int x = 0; if( y == y1 ) { x = mPA.x(); } for( ; ; x++ ) { if( (y == mPB.y()) && (x > mPB.x()) ) { break; } mpBuffer->dirty[y] = true; if( x < static_cast(mpBuffer->buffer[y].size()) ) { mpBuffer->buffer[y][x].flags |= TCHAR_INVERSE; } else { break; } } } update( mSelectedRegion.boundingRect() ); mSelectedRegion = newRegion; } void TTextEdit::unHighlight( QRegion & region ) { int y1 = mPA.y(); if( y1 < 0 ) { return; } for( int y=y1; y<=mPB.y(); y++ ) { int x = 0; if( y == y1 ) { x = mPA.x(); } for( ; ; x++ ) { if( (y == mPB.y()) && (x > mPB.x()) ) { break; } if( y >= static_cast(mpBuffer->buffer.size()) ) { break; } mpBuffer->dirty[y] = true; if( x < static_cast(mpBuffer->buffer[y].size()) ) { mpBuffer->buffer[y][x].flags &= ~(TCHAR_INVERSE); mpBuffer->dirty[y] = true; } else { break; } } } mForceUpdate = true; update(); } void TTextEdit::swap( QPoint & p1, QPoint & p2 ) { QPoint tmp = p1; p1 = p2; p2 = tmp; } void TTextEdit::mouseMoveEvent( QMouseEvent * event ) { if( (mFontWidth == 0) | (mFontHeight == 0) ) return; int x = event->x() / mFontWidth;// bugfix by BenH (used to be mFontWidth-1) if( mShowTimeStamps ) { x -= 13; } int y = ( event->y() / mFontHeight ) + imageTopLine(); if( x < 0 ) x = 0; if( y < 0 ) y = 0; if( y < static_cast(mpBuffer->buffer.size()) ) { if( x < static_cast(mpBuffer->buffer[y].size()) ) { if( mpBuffer->buffer[y][x].link > 0 ) { setCursor( Qt::PointingHandCursor ); QStringList tooltip = mpBuffer->mHintStore[mpBuffer->buffer[y][x].link]; QToolTip::showText( event->globalPos(), tooltip.join("\n") ); } else { setCursor( Qt::IBeamCursor ); QToolTip::hideText(); } } } if( ! mMouseTracking ) return; if( event->y() < 10 ) { mpConsole->scrollUp( 3 ); } if( event->y() >= height()-10 ) { mpConsole->scrollDown( 3 ); } if( ( y > (int) mpBuffer->size()-1 ) ) { return; } QPoint PC( x, y ); if( mCtrlSelecting ) { int oldAY = mPA.y(); int oldBY = mPB.y(); if( PC.y() == mDragStartY ){ mPA.setY( PC.y() ); mPB.setY( PC.y() ); } else if( PC.y() < mDragStartY ){ mPA.setY( PC.y() ); mPB.setY( mDragStartY ); } else if( PC.y() > mDragStartY ){ mPA.setY( mDragStartY ); mPB.setY( PC.y() ); } if( oldAY < mPA.y() ){ for( int y = oldAY; y < mPA.y(); y++ ){ for(auto & x : mpBuffer->buffer[y]){ x.flags &= ~(TCHAR_INVERSE); } } } if( oldBY > mPB.y() ){ for( int y = mPB.y()+1; y <= oldBY; y++ ){ for(auto & x : mpBuffer->buffer[y]){ x.flags &= ~(TCHAR_INVERSE); } } } mPA.setX( 0 ); mPB.setX( static_cast(mpBuffer->buffer[mPB.y()].size())-1 ); highlight(); return; } if( ( mPA.y() == mPB.y() ) && ( mPA.x() > mPB.x() ) ) { swap( mPA, mPB ); } if( mPA.y() > mPB.y() ) { swap( mPA, mPB ); } QPoint p1 = mPA-PC; QPoint p2 = mPB-PC; if( p1.manhattanLength() < p2.manhattanLength() ) { if( mPA.y() < PC.y() || ( (mPA.x() < PC.x()) && (mPA.y() == PC.y()) ) ) { int y1 = PC.y(); for( int y=y1; y>=mPA.y(); y-- ) { if( y >= static_cast(mpBuffer->buffer.size()) || y < 0 ) break; int x = mpBuffer->buffer[y].size()-1; if( y == y1 ) { x = PC.x(); if( x >= static_cast(mpBuffer->buffer[y].size()) ) x = static_cast(mpBuffer->buffer[y].size())-1; if( x < 0 ) x = 0; } mpBuffer->dirty[y] = true; for( ; ; x-- ) { if( ( y == mPA.y() ) && ( x < mPA.x() ) ) { break; } if( x < static_cast(mpBuffer->buffer[y].size()) && x >= 0 ) { mpBuffer->buffer[y][x].flags &= ~(TCHAR_INVERSE); } else { break; } } } mP_aussen = mPA; } else mP_aussen = PC; mPA = PC; } else { if( mPB.y() > PC.y() || (mPB.x() > PC.x() && mPB.y() == PC.y()) ) { int y1 = PC.y(); for( int y=y1; y<=mPB.y(); y++ ) { int x = 0; if( y == y1 ) { x = PC.x(); } if( y >= static_cast(mpBuffer->buffer.size()) || y < 0 ) break; mpBuffer->dirty[y] = true; for( ; ; x++ ) { if( ( y == mPB.y() ) && ( x > mPB.x() ) ) { break; } if( x < static_cast(mpBuffer->buffer[y].size()) ) { mpBuffer->buffer[y][x].flags &= ~(TCHAR_INVERSE); } else { break; } } } mP_aussen = mPB; } else mP_aussen = PC; mPB = PC; } if( ( mPA.y() == mPB.y() ) && ( mPA.x() > mPB.x() ) ) { swap( mPA, mPB ); } if( mPA.y() > mPB.y() ) { swap( mPA, mPB ); } mP_aussen = PC; highlight(); } void TTextEdit::contextMenuEvent( QContextMenuEvent * event ) { event->accept(); return; } void TTextEdit::slot_popupMenu() { QAction * pA = (QAction *)sender(); if( ! pA ) { return; } QString cmd; if( mPopupCommands.contains( pA->text() ) ) { cmd = mPopupCommands[pA->text()]; } mpHost->mLuaInterpreter.compileAndExecuteScript( cmd ); } void TTextEdit::mousePressEvent( QMouseEvent * event ) { if (!mpConsole->mIsSubConsole && !mpConsole->mIsDebugConsole) { TEvent mudletEvent; mudletEvent.mArgumentList.append(QLatin1String("sysWindowMousePressEvent")); switch (event->button()) { case Qt::LeftButton: mudletEvent.mArgumentList.append(QString::number(1)); break; case Qt::RightButton: mudletEvent.mArgumentList.append(QString::number(2)); break; case Qt::MidButton: mudletEvent.mArgumentList.append(QString::number(3)); break; default: // TODO: What about those of us with more than three mouse buttons? mudletEvent.mArgumentList.append(0); break; } mudletEvent.mArgumentList.append(QString::number(event->x())); mudletEvent.mArgumentList.append(QString::number(event->y())); mudletEvent.mArgumentTypeList.append(ARGUMENT_TYPE_STRING); mudletEvent.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER); mudletEvent.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER); mudletEvent.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER); mpHost->raiseEvent(mudletEvent); } if( event->button() == Qt::LeftButton ) { if( event->modifiers() & Qt::ControlModifier ) { mCtrlSelecting = true; } int x = event->x() / mFontWidth; if( mShowTimeStamps ) { if( x < 13 ) { mCtrlSelecting = true; } x -= 13; } int y = ( event->y() / mFontHeight ) + imageTopLine(); if( x < 0 ) x = 0; if( y < 0 ) y = 0; if( y < static_cast(mpBuffer->buffer.size()) ) { if( x < static_cast(mpBuffer->buffer[y].size()) ) { if( mpBuffer->buffer[y][x].link > 0 ) { QStringList command = mpBuffer->mLinkStore[mpBuffer->buffer[y][x].link]; QString func; if( command.size() > 0 ) { func = command.at(0); mpHost->mLuaInterpreter.compileAndExecuteScript( func ); return; } } } } unHighlight( mSelectedRegion ); mSelectedRegion = QRegion( 0, 0, 0, 0 ); if ( mLastClickTimer.elapsed() < 300 ) { int xind=x; int yind=y; if ( yind >= mpBuffer->lineBuffer.size() ) return; if ( xind >= mpBuffer->lineBuffer[yind].size() ) return; while( xind < static_cast( mpBuffer->buffer[yind].size() ) ) { QChar c = mpBuffer->lineBuffer[yind].at(xind); if ( c == ' ' ) break; xind++; } // For ignoring user specified characters, we first stop at space boundaries, then we // proceed to search within these spaces for ignored characters and chop off any we find. while(xind>0 && mpHost->mDoubleClickIgnore.contains(mpBuffer->lineBuffer[yind].at(xind-1))) xind--; mPB.setX ( xind-1 ); mPB.setY ( yind ); for( xind=x-1; xind>0; xind--) { QChar c = mpBuffer->lineBuffer[yind].at(xind); if (c == ' ') break; } int lsize = mpBuffer->lineBuffer[yind].size(); while(xind+1 < lsize && mpHost->mDoubleClickIgnore.contains(mpBuffer->lineBuffer[yind].at(xind+1))) xind++; if ( xind > 0 ) mPA.setX ( xind+1 ); else mPA.setX ( qMax( 0, xind ) ); mPA.setY ( yind ); highlight(); event->accept(); return; } else { mLastClickTimer.start(); mMouseTracking = true; if( y >= mpBuffer->size() ) { return; } if( mCtrlSelecting ) { mPA.setX( 0 ); mPA.setY( y ); mPB.setX( static_cast(mpBuffer->buffer[y].size())-1 ); mPB.setY( y ); mDragStartY = y; highlight(); } else { mPA.setX( x ); mPA.setY( y ); mPB = mPA; } event->accept(); return; } } if( event->button() == Qt::RightButton ) { int x = event->x() / mFontWidth; if( mShowTimeStamps ) { x -= 13; } int y = ( event->y() / mFontHeight ) + imageTopLine(); if( x < 0 ) x = 0; if( y < 0 ) y = 0; if( y < static_cast(mpBuffer->buffer.size()) ) { if( x < static_cast(mpBuffer->buffer[y].size()) ) { if( mpBuffer->buffer[y][x].link > 0 ) { QStringList command = mpBuffer->mLinkStore[mpBuffer->buffer[y][x].link]; QStringList hint = mpBuffer->mHintStore[mpBuffer->buffer[y][x].link]; if( command.size() > 1 ) { auto popup = new QMenu( this ); for( int i=0; i< hint.size() ) { pA = popup->addAction( hint[i] ); mPopupCommands[hint[i]] = command[i]; } else { pA = popup->addAction( command[i] ); mPopupCommands[command[i]] = command[i]; } connect( pA, SIGNAL(triggered()), this, SLOT(slot_popupMenu())); } popup->popup( event->globalPos() ); } mIsCommandPopup = true; return; } } } mIsCommandPopup = false; QAction * action = new QAction("copy", this ); action->setStatusTip(tr("copy selected text to clipboard")); connect( action, SIGNAL(triggered()), this, SLOT(slot_copySelectionToClipboard())); QAction * action2 = new QAction("copy HTML", this ); action2->setStatusTip(tr("copy selected text with colors as HTML (for web browsers)")); connect( action2, SIGNAL(triggered()), this, SLOT(slot_copySelectionToClipboardHTML())); auto popup = new QMenu( this ); popup->addAction( action ); popup->addAction( action2 ); popup->popup( mapToGlobal( event->pos() ), action ); event->accept(); return; } if( event->button() == Qt::MidButton ) { mpConsole->console2->mCursorY = mpConsole->buffer.size();// mpConsole->console2->hide(); mpBuffer->mCursorY = mpBuffer->size(); mpConsole->console->mCursorY = mpConsole->buffer.size();// mpConsole->console->mIsTailMode = true; mpConsole->console->updateScreenView(); mpConsole->console->forceUpdate(); event->accept(); return; } QWidget::mousePressEvent( event ); } void TTextEdit::slot_copySelectionToClipboard() { copySelectionToClipboard(); } void TTextEdit::slot_copySelectionToClipboardHTML() { copySelectionToClipboardHTML(); } void TTextEdit::copySelectionToClipboard() { if( ( mPA.y() == mPB.y() ) && ( mPA.x() > mPB.x() ) ) { swap( mPA, mPB ); } if( mPA.y() > mPB.y() ) { swap( mPA, mPB ); } QString text; for( int y=mPA.y(); y<=mPB.y()+1; y++ ) { if( y >= static_cast(mpBuffer->buffer.size()) ) { QClipboard * clipboard = QApplication::clipboard(); clipboard->setText( text ); mSelectedRegion = QRegion( 0, 0, 0, 0 ); forceUpdate(); return; } // add timestamps to clipboard when "Show Time Stamps" is on and it is not one-line selection if (mShowTimeStamps && !mpBuffer->timeBuffer[y].isEmpty() && mPA.y() != mPB.y()) { text.append( mpBuffer->timeBuffer[y].left(13) ); } int x = 0; if( y == mPA.y() ) x = mPA.x(); while( x < static_cast( mpBuffer->buffer[y].size() ) ) { text.append( mpBuffer->lineBuffer[y].at(x) ); if( y >= mPB.y() ) { if( ( x == mPB.x() ) || ( x >= static_cast( mpBuffer->buffer[y].size() - 1 ) ) ) { QClipboard * clipboard = QApplication::clipboard(); clipboard->setText( text ); mSelectedRegion = QRegion( 0, 0, 0, 0 ); forceUpdate(); return; } } x++; } text.append("\n"); } } void TTextEdit::copySelectionToClipboardHTML() { if( ( mPA.y() == mPB.y() ) && ( mPA.x() > mPB.x() ) ) { swap( mPA, mPB ); } if( mPA.y() > mPB.y() ) { swap( mPA, mPB ); } QString title; if( this->mIsDebugConsole ) { title = tr( "Mudlet, debug console extract" ); } else if( this->mIsMiniConsole ) { if( ! this->mpHost->mpConsole->mSubConsoleMap.empty() ) { for( auto it = this->mpHost->mpConsole->mSubConsoleMap.cbegin(); it != this->mpHost->mpConsole->mSubConsoleMap.cend(); ++it ) { if( (*it).second == this->mpConsole ) { title = tr( "Mudlet, %1 mini-console extract from %2 profile" ).arg((*it).first.data(), this->mpHost->getName() ); break; } } } } else { title = tr( "Mudlet, %1 console extract from %2 profile" ).arg(this->mpConsole->mConsoleName, this->mpHost->getName() ); } QStringList fontsList; // List of fonts to become the font-family entry for // the master css in the header fontsList << this->fontInfo().family(); // Seems to be the best way to get the // font in use, as different TConsole // instances within the same profile // might have different fonts in future, // and although the font is settable for // the main profile window, it is not yet // for user miniConsoles, or the Debug one fontsList << QStringLiteral( "Courier New" ); fontsList << QStringLiteral( "Monospace" ); fontsList << QStringLiteral( "Courier" ); fontsList.removeDuplicates(); // In case the actual one is one of the defaults here QString text = "\n"; text.append("\n"); text.append(" \n"); text.append(" "); // put the charset as early as possible as the parser MUST restart when it // switches away from the ASCII default text.append(" \n"); // Nice to identify what made the file! text.append(" "); text.append(title); text.append("\n"); // Web-page title text.append(" \n"); text.append(" \n"); text.append("
"); //
tags required around outside of the body for // strict HTML 4 as we do not use

s or anything else for( int y=mPA.y(); y<=mPB.y(); y++ ) { if( y >= static_cast(mpBuffer->buffer.size()) ) { return; } int x = 0; if( y == mPA.y() ) {// First line of selection x = mPA.x(); text.append(mpBuffer->bufferToHtml( QPoint(x,y), QPoint(-1,y), mShowTimeStamps, x)); } else if ( y == mPB.y() ) {// Last line of selection x = mPB.x(); text.append(mpBuffer->bufferToHtml( QPoint(0,y), QPoint(x,y), mShowTimeStamps)); } else { // inside lines of selection text.append(mpBuffer->bufferToHtml( QPoint(0,y), QPoint(-1,y), mShowTimeStamps)); } } text.append( QStringLiteral( "
\n" "" ) ); // The last two of these tags were missing and meant the HTML was not terminated properly QClipboard * clipboard = QApplication::clipboard(); clipboard->setText( text ); mSelectedRegion = QRegion( 0, 0, 0, 0 ); forceUpdate(); return; } void TTextEdit::mouseReleaseEvent( QMouseEvent * event ) { if (event->button() == Qt::LeftButton) { mMouseTracking = false; mCtrlSelecting = false; } if (!mpConsole->mIsSubConsole && !mpConsole->mIsDebugConsole) { TEvent mudletEvent; mudletEvent.mArgumentList.append(QLatin1String("sysWindowMouseReleaseEvent")); switch (event->button()) { case Qt::LeftButton: mudletEvent.mArgumentList.append(QString::number(1)); break; case Qt::RightButton: mudletEvent.mArgumentList.append(QString::number(2)); break; case Qt::MidButton: mudletEvent.mArgumentList.append(QString::number(3)); break; default: mudletEvent.mArgumentList.append(QString::number(0)); break; } mudletEvent.mArgumentList.append(QString::number(event->x())); mudletEvent.mArgumentList.append(QString::number(event->y())); mudletEvent.mArgumentTypeList.append(ARGUMENT_TYPE_STRING); mudletEvent.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER); mudletEvent.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER); mudletEvent.mArgumentTypeList.append(ARGUMENT_TYPE_NUMBER); mpHost->raiseEvent(mudletEvent); } } void TTextEdit::showEvent( QShowEvent * event ) { updateScreenView(); mScrollVector=0; repaint(); QWidget::showEvent( event ); } void TTextEdit::resizeEvent( QResizeEvent * event ) { updateScreenView(); if( ! mIsSplitScreen && ! mIsDebugConsole ) { mpHost->adjustNAWS(); } QWidget::resizeEvent( event ); } void TTextEdit::wheelEvent( QWheelEvent * e ) { int k = 3; if( e->delta() < 0 ) { mpConsole->scrollDown( abs( k ) ); e->accept(); return; } if( e->delta() > 0 ) { mpConsole->scrollUp( k ); e->accept(); return; } e->ignore(); return; } int TTextEdit::imageTopLine() { if( ! mIsSplitScreen ) { mCursorY = mpBuffer->mCursorY; } if( mCursorY > mScreenHeight ) { if( isTailMode() ) { if( mpBuffer->lineBuffer[mpBuffer->getLastLineNumber()] == "" ) { return mCursorY - mScreenHeight - 1; } else { return mCursorY - mScreenHeight; } } else { return mCursorY - mScreenHeight; } } else { return 0; } } bool TTextEdit::isTailMode() { if( mIsTailMode ) { mIsTailMode = true; return true; } else { return false; } } int TTextEdit::bufferScrollUp( int lines ) { if( (mpBuffer->mCursorY - lines) >= mScreenHeight ) { mpBuffer->mCursorY -= lines; mIsTailMode = true; return lines; } else { mpBuffer->mCursorY -= lines; if( mCursorY < 0 ) { int delta = mCursorY; mpBuffer->mCursorY = 0; return delta; } else return 0; } } int TTextEdit::bufferScrollDown( int lines ) { if( ( mpBuffer->mCursorY + lines ) < (int)(mpBuffer->size()) ) { if( mpBuffer->mCursorY + lines < mScreenHeight + lines ) { mpBuffer->mCursorY = mScreenHeight+lines; if( mpBuffer->mCursorY > (int)(mpBuffer->size()-1 ) ) { mpBuffer->mCursorY = mpBuffer->size()-1; mIsTailMode = true; } } else { mpBuffer->mCursorY += lines; mIsTailMode = false; } return lines; } else if( mpBuffer->mCursorY >= (int)(mpBuffer->size()-1) ) { mIsTailMode = true; mpBuffer->mCursorY = mpBuffer->lineBuffer.size(); forceUpdate(); return 0; } else { lines = (int)(mpBuffer->size()-1) - mpBuffer->mCursorY; if( mpBuffer->mCursorY + lines < mScreenHeight + lines ) { mpBuffer->mCursorY = mScreenHeight+lines; if( mpBuffer->mCursorY > (int)(mpBuffer->size()-1 ) ) { mpBuffer->mCursorY = mpBuffer->size()-1; mIsTailMode = true; } } else { mpBuffer->mCursorY += lines; mIsTailMode = false; } return lines; } }