summaryrefslogtreecommitdiff
path: root/src/fqterm/fqterm_screen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/fqterm/fqterm_screen.cpp')
-rw-r--r--src/fqterm/fqterm_screen.cpp1868
1 files changed, 1868 insertions, 0 deletions
diff --git a/src/fqterm/fqterm_screen.cpp b/src/fqterm/fqterm_screen.cpp
new file mode 100644
index 0000000..339e666
--- /dev/null
+++ b/src/fqterm/fqterm_screen.cpp
@@ -0,0 +1,1868 @@
+/****************************************************************************
+ * fqterm, a terminal emulator for both BBS and *nix. *
+ * Copyright (C) 2008 fqterm development group. *
+ * *
+ * 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., *
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. *
+ ***************************************************************************/
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <QApplication>
+#include <QPainter>
+#include <QScrollBar>
+#include <QShortcut>
+#include <QTextCharFormat>
+#include <QTimer>
+#include <QWheelEvent>
+#include <QKeySequence>
+#include <QRegion>
+#include <QPolygon>
+
+#include "fqterm_buffer.h"
+#include "fqterm_config.h"
+#include "fqterm_convert.h"
+#include "fqterm_frame.h"
+#include "fqterm_param.h"
+#include "fqterm_screen.h"
+#include "fqterm_session.h"
+#include "fqterm_text_line.h"
+#include "fqterm_window.h"
+#include "fqterm_wndmgr.h"
+#ifdef HAVE_PYTHON
+#include <Python.h>
+#endif //HAVE_PYTHON
+namespace FQTerm {
+/* ------------------------------------------------------------------------ */
+/* */
+/* Constructor/Destructor */
+/* */
+/* ------------------------------------------------------------------------ */
+
+FQTermScreen::FQTermScreen(QWidget *parent, FQTermSession *session)
+ : QWidget(parent),
+ scrollBarWidth_(15),
+ termBuffer_(session->getBuffer()),
+ cnLetterSpacing_(0.0),
+ spLetterSpacing_(0.0),
+ enLetterSpacing_(0.0),
+ cnFixedPitch_(false),
+ enFixedPitch_(false),
+ hasBackground_(false),
+ backgroundRenderOption_(0),
+ backgroundCoverage_(0),
+ backgroundAlpha_(0),
+ backgroundUseAlpha_(false) {
+ termWindow_ = (FQTermWindow*)parent;
+ session_ = session;
+ param_ = &session->param();
+ paintState_ = System;
+ isCursorShown_ = true;
+ is_light_background_mode_ = false;
+
+ setFocusPolicy(Qt::ClickFocus);
+ setAttribute(Qt::WA_InputMethodEnabled, true);
+ setMouseTracking(true);
+
+ setSchema();
+
+ initFontMetrics();
+
+ FQ_VERIFY(connect(termBuffer_, SIGNAL(bufferSizeChanged()),
+ this, SLOT(bufferSizeChanged())));
+
+ FQ_VERIFY(connect(termBuffer_, SIGNAL(termSizeChanged(int, int)),
+ this, SLOT(termSizeChanged(int, int))));
+
+ blinkTimer_ = new QTimer(this);
+ FQ_VERIFY(connect(blinkTimer_, SIGNAL(timeout()), this, SLOT(blinkEvent())));
+
+ cursorTimer_ = new QTimer(this);
+ FQ_VERIFY(connect(cursorTimer_, SIGNAL(timeout()),
+ this, SLOT(cursorEvent())));
+
+ // the scrollbar
+ scrollBar_ = new QScrollBar(this);
+ scrollBar_->setCursor(Qt::ArrowCursor);
+ bufferStart_ = 0;
+ bufferEnd_ = termBuffer_->getNumRows() - 1;
+ areLinesBlink_ = new bool[bufferEnd_ - bufferStart_ + 1];
+ scrollBar_->setRange(0, 0);
+ scrollBar_->setSingleStep(1);
+ scrollBar_->setPageStep(termBuffer_->getNumRows());
+ scrollBar_->setValue(0);
+ updateScrollBar();
+ FQ_VERIFY(connect(scrollBar_, SIGNAL(valueChanged(int)),
+ this, SLOT(scrollChanged(int))));
+
+ gotoPrevPage_ = new QShortcut(QKeySequence(tr("Shift+PageUp")),
+ this, SLOT(prevPage()));
+ gotoNextPage_ = new QShortcut(QKeySequence(tr("Shift+PageDown")),
+ this, SLOT(nextPage()));
+ gotoPrevLine_ = new QShortcut(QKeySequence(tr("Shift+Up")),
+ this, SLOT(prevLine()));
+ gotoNextLine_ = new QShortcut(QKeySequence(tr("Shift+Down")),
+ this, SLOT(nextLine()));
+
+ setAttribute(Qt::WA_OpaquePaintEvent, true);
+
+ // init variable
+ isBlinkScreen_ = false;
+ isBlinkCursor_ = true;
+
+
+ preedit_line_ = new PreeditLine(this, colors_);
+
+ tmp_im_query_ = new QString();
+}
+
+FQTermScreen::~FQTermScreen() {
+ delete [] areLinesBlink_;
+ delete blinkTimer_;
+ delete cursorTimer_;
+ //delete m_pCanvas;
+ //delete m_inputContent;
+
+ delete preedit_line_;
+
+ delete tmp_im_query_;
+}
+
+bool FQTermScreen::event(QEvent *e) {
+ switch(e->type()) {
+ case QEvent::KeyPress:
+ {
+ // forward all key press events to parant (FQTermWindow).
+ return false;
+ }
+ }
+ return this->QWidget::event(e);
+}
+
+// focus event received
+void FQTermScreen::focusInEvent(QFocusEvent*) {
+
+ gotoPrevPage_->setEnabled(true);
+ gotoNextPage_->setEnabled(true);
+ gotoPrevLine_->setEnabled(true);
+ gotoNextLine_->setEnabled(true);
+
+}
+
+// focus out
+void FQTermScreen::focusOutEvent(QFocusEvent*) {
+ gotoPrevPage_->setEnabled(false);
+ gotoNextPage_->setEnabled(false);
+ gotoPrevLine_->setEnabled(false);
+ gotoNextLine_->setEnabled(false);
+}
+
+void FQTermScreen::resizeEvent(QResizeEvent*) {
+ syncBufferAndScreen();
+}
+
+void FQTermScreen::syncBufferAndScreen() {
+ updateScrollBar();
+ updateBackgroundPixmap();
+
+ if (param_->isFontAutoFit_ == 1) {
+ updateFont();
+ } else if (param_->isFontAutoFit_ == 0) { //adjust column/row
+ int cx = clientRectangle_.width() / charWidth_;
+ int cy = clientRectangle_.height() / charHeight_;
+ if (param_->hostType_ == 0) {
+ if (cx < 80) cx = 80;
+ if (cy < 24) cy = 24;
+ } else {
+ if (cx < 10) cx = 10;
+ if (cy < 10) cy = 10;
+ }
+ session_->setTermSize(cx, cy);
+ //session_->writeStr("\0x5f");
+ } else {
+ session_->setTermSize(param_->numColumns_, param_->numRows_);
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* */
+/* Mouse */
+/* */
+/* ------------------------------------------------------------------------ */
+void FQTermScreen::enterEvent(QEvent *e) {
+ QApplication::sendEvent(termWindow_, e);
+}
+
+void FQTermScreen::leaveEvent(QEvent *e) {
+ QApplication::sendEvent(termWindow_, e);
+}
+
+void FQTermScreen::mousePressEvent(QMouseEvent *me) {
+
+ termWindow_->mousePressEvent(me);
+ setFocus();
+}
+
+void FQTermScreen::mouseMoveEvent(QMouseEvent *me) {
+#ifdef Q_OS_MACX
+ termWindow_->mouseMoveEvent(me);
+#else
+ termWindow_->mouseMoveEvent(me);
+ //QApplication::sendEvent(m_pWindow, me);
+#endif
+}
+
+void FQTermScreen::mouseReleaseEvent(QMouseEvent *me) {
+ termWindow_->mouseReleaseEvent(me);
+ //QApplication::sendEvent(m_pWindow, me);
+}
+
+void FQTermScreen::wheelEvent(QWheelEvent *we) {
+ if (FQTermPref::getInstance()->isWheelSupported_) {
+ QApplication::sendEvent(termWindow_, we);
+ } else {
+ QApplication::sendEvent(scrollBar_, we);
+ }
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* */
+/* Font */
+/* */
+/* ------------------------------------------------------------------------ */
+
+void FQTermScreen::initFontMetrics() {
+ //issue 98
+ if (param_->isFontAutoFit_ == 1) {
+ englishFont_ = new QFont(param_->englishFontName_);
+ nonEnglishFont_ = new QFont(param_->otherFontName_);
+
+ updateFont();
+ } else {
+ englishFont_ = new QFont(param_->englishFontName_, qMax(8, param_->englishFontSize_));
+ nonEnglishFont_ = new QFont(param_->otherFontName_, qMax(8, param_->otherFontSize_));
+
+ setFontMetrics();
+ }
+
+ englishFont_->setWeight(QFont::Normal);;
+ nonEnglishFont_->setWeight(QFont::Normal);
+
+ // m_pFont->setStyleHint(QFont::System,
+ // m_pWindow->m_pFrame->m_pref.bAA ?
+ // QFont::PreferAntialias : QFont::NoAntialias);
+ updateFixedPitchInfo();
+}
+
+void FQTermScreen::updateFont() {
+ int nPixelSize;
+ int nIniSize =
+ qMax(8, qMin(clientRectangle_.height() / termBuffer_->getNumRows(),
+ clientRectangle_.width() *2 / termBuffer_->getNumColumns()));
+ //FIXME: WTF????
+ for (nPixelSize = nIniSize - 3; nPixelSize <= nIniSize + 3; nPixelSize++) {
+ englishFont_->setPixelSize(nPixelSize);
+ nonEnglishFont_->setPixelSize(nPixelSize);
+
+ setFontMetrics();
+ if ((termBuffer_->getNumRows() *charHeight_) > clientRectangle_.height() ||
+ (termBuffer_->getNumColumns() *charWidth_) > clientRectangle_.width()) {
+ while (nPixelSize > 5) {
+ nPixelSize--;
+ englishFont_->setPixelSize(nPixelSize);
+ nonEnglishFont_->setPixelSize(nPixelSize);
+
+ setFontMetrics();
+ //changed by dp to get larger font...
+ if ((termBuffer_->getNumRows()*charHeight_) <= clientRectangle_.height()
+ && (termBuffer_->getNumColumns() *charWidth_) <=
+ clientRectangle_.width()) {
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ englishFont_->setWeight(QFont::Normal);
+ nonEnglishFont_->setWeight(QFont::Normal);
+ setFontAntiAliasing(FQTermPref::getInstance()->openAntiAlias_);
+}
+
+void FQTermScreen::setFontMetrics() {
+
+ QFontMetrics nonEnglishFM(*nonEnglishFont_);
+ QFontMetrics englishFM(*englishFont_);
+
+ // FIXME: find a typical character for the current language.
+ int cn = nonEnglishFM.width(QChar(0x4e2D));
+ int en = englishFM.width('W');
+
+ int pix_size = nonEnglishFont_->pixelSize();
+ while (cn % 2 && pix_size > 10) {
+ nonEnglishFont_->setPixelSize(--pix_size);
+ nonEnglishFM = QFontMetrics(*nonEnglishFont_);
+ cn = nonEnglishFM.width(QChar(0x4e2D));
+ }
+
+ pix_size = englishFont_->pixelSize();
+ while (2 * en > cn + 1 && pix_size > 5) {
+ --pix_size;
+ englishFont_->setPixelSize(pix_size);
+ englishFM = QFontMetrics(*englishFont_);
+ en = englishFM.width('W');
+/*
+#ifndef __APPLE__
+ //FIXME: correctly draw chars with left/right bearing.
+ if ( - englishFM.leftBearing('W') - englishFM.rightBearing('W') > 0) {
+ en += - englishFM.leftBearing('W') - englishFM.rightBearing('W');
+ }
+#endif
+*/
+ }
+
+ charWidth_ = qMax(float(cn) / 2 , float(en));
+
+ charHeight_ = qMax(englishFM.height(), nonEnglishFM.height());
+ charHeight_ += param_->lineSpacing_;
+ //charWidth_ = ceil(qMax(charWidth_, charHeight_ * param_->charRatio_ / 100));
+ charWidth_ += param_->charSpacing_;
+
+ cnLetterSpacing_ = qMax(charWidth_ * 2 - cn, 0.0);
+ enLetterSpacing_ = qMax(charWidth_ - en, 0.0);
+
+ spLetterSpacing_ = qMax(charWidth_ - englishFM.width(' '), 0.0);
+
+ fontAscent_ = qMax(englishFM.ascent(), nonEnglishFM.ascent());
+ fontDescent_ = qMax(englishFM.descent(), nonEnglishFM.descent());
+
+ FQ_TRACE("font", 4) << "\nNon-English font:"
+ << "\nheight: " << nonEnglishFM.height()
+ << "\nascent: " << nonEnglishFM.ascent()
+ << "\ndescent: " << nonEnglishFM.descent();
+
+
+ FQ_TRACE("font", 4) << "\nEnglish font:"
+ << "\nheight: " << englishFM.height()
+ << "\nascent: " << englishFM.ascent()
+ << "\ndescent: " << englishFM.descent();
+
+ updateFixedPitchInfo();
+}
+
+/* ------------------------------------------------------------------------ */
+/* */
+/* Colors */
+/* */
+/* ------------------------------------------------------------------------ */
+void FQTermScreen::setSchema() {
+ // the default color table
+ colors_[0] = Qt::black;
+ colors_[1] = Qt::darkRed;
+ colors_[2] = Qt::darkGreen;
+ colors_[3] = Qt::darkYellow;
+ colors_[4] = Qt::darkBlue;
+ colors_[5] = Qt::darkMagenta;
+ colors_[6] = Qt::darkCyan;
+ colors_[7] = Qt::darkGray;
+ colors_[8] = Qt::gray;
+ colors_[9] = Qt::red;
+ colors_[10] = Qt::green;
+ colors_[11] = Qt::yellow;
+ colors_[12] = Qt::blue;
+ colors_[13] = Qt::magenta;
+ colors_[14] = Qt::cyan;
+ colors_[15] = Qt::white;
+
+
+ hasBackground_ = false;
+ // if we have schema defined
+ if (QFile::exists(param_->schemaFileName_)) {
+ FQTermConfig *pConf = new FQTermConfig(param_->schemaFileName_);
+ for (int i = 0; i < 16; ++i) {
+ colors_[i].setNamedColor(pConf->getItemValue("color", QString("color%1").arg(i)));
+ }
+ originBackgroundPixmap_ = QPixmap(pConf->getItemValue("image", "name"));
+ //0 -- none 1 -- image
+ hasBackground_ = pConf->getItemValue("background", "type").toInt() ? 1 : 0;
+ backgroundRenderOption_ = pConf->getItemValue("image", "render").toInt();
+ backgroundCoverage_ = pConf->getItemValue("image", "cover").toInt();
+ backgroundUseAlpha_ = pConf->getItemValue("image", "usealpha").toInt();
+ backgroundAlpha_ = pConf->getItemValue("image", "alpha").toInt();
+
+ delete pConf;
+ }
+
+ // override schema using user defined Fg/Bg color
+ colors_[0] = param_->backgroundColor_;
+ colors_[7] = param_->foregroundColor_;
+}
+
+/* ------------------------------------------------------------------------ */
+/* */
+/* Scrollbar */
+/* */
+/* ------------------------------------------------------------------------*/
+void FQTermScreen::prevPage() {
+ scrollLine(-termBuffer_->getNumRows());
+ setPaintState(NewData);
+ update();
+}
+
+void FQTermScreen::nextPage() {
+ scrollLine(termBuffer_->getNumRows());
+ setPaintState(NewData);
+ update();
+}
+
+void FQTermScreen::prevLine() {
+ scrollLine(-1);
+ setPaintState(NewData);
+ update();
+}
+
+void FQTermScreen::nextLine() {
+ scrollLine(1);
+ setPaintState(NewData);
+ update();
+}
+
+void FQTermScreen::scrollLine(int delta) {
+ bufferStart_ += delta;
+
+ if (bufferStart_ < 0) {
+ bufferStart_ = 0;
+ return ;
+ }
+ if (bufferStart_ > termBuffer_->getNumLines() - termBuffer_->getNumRows()) {
+ bufferStart_ = termBuffer_->getNumLines() - termBuffer_->getNumRows();
+ return ;
+ }
+
+ scrollBar_->setValue(bufferStart_);
+ bufferEnd_ = bufferStart_ + termBuffer_->getNumRows() - 1;
+
+ // notify session
+ session_->setScreenStart(bufferStart_);
+
+ for (int i = bufferStart_; i <= bufferEnd_; i++) {
+ session_->setLineAllChanged(i);
+ }
+}
+
+void FQTermScreen::scrollChanged(int value) {
+ if (bufferStart_ == value) {
+ return ;
+ }
+
+ if (value < 0) {
+ value = 0;
+ }
+ if (value > termBuffer_->getNumLines() - termBuffer_->getNumRows()) {
+ value = termBuffer_->getNumLines() - termBuffer_->getNumRows();
+ }
+
+ bufferStart_ = value;
+ bufferEnd_ = value + termBuffer_->getNumRows() - 1;
+
+ // notify session
+ session_->setScreenStart(bufferStart_);
+
+ for (int i = bufferStart_; i <= bufferEnd_; i++) {
+ //termBuffer_->at(i)->setChanged(-1, -1);
+ session_->setLineAllChanged(i);
+ }
+
+ setPaintState(NewData);
+ update();
+}
+
+void FQTermScreen::updateScrollBar() {
+ int numLeftPixels = 0; // ߾
+ int numUpPixels = 0; // ϱ߾
+
+ switch (FQTermPref::getInstance()->termScrollBarPosition_) {
+ case 0:
+ scrollBar_->hide();
+ clientRectangle_ = QRect(numLeftPixels, numUpPixels,
+ rect().width() - numLeftPixels,
+ rect().height() - numUpPixels);
+ break;
+ case 1:
+ // LEFT
+ scrollBar_->setGeometry(0, 0, scrollBarWidth_, rect().height());
+ scrollBar_->show();
+ clientRectangle_ = QRect(scrollBarWidth_ + numLeftPixels, numUpPixels,
+ rect().width() - scrollBarWidth_ - numLeftPixels,
+ rect().height() - numUpPixels);
+ break;
+ case 2:
+ // RIGHT
+ scrollBar_->setGeometry(rect().width() - scrollBarWidth_, 0,
+ scrollBarWidth_, rect().height());
+ scrollBar_->show();
+ clientRectangle_ = QRect(numLeftPixels, numUpPixels,
+ rect().width() - scrollBarWidth_ - numLeftPixels,
+ rect().height() - numUpPixels);
+ break;
+ }
+ setPaintState(Repaint);
+ update();
+}
+
+void FQTermScreen::bufferSizeChanged() {
+ FQ_VERIFY(disconnect(scrollBar_, SIGNAL(valueChanged(int)),
+ this, SLOT(scrollChanged(int))));
+ scrollBar_->setRange(0, termBuffer_->getNumLines() - termBuffer_->getNumRows());
+ scrollBar_->setSingleStep(1);
+ scrollBar_->setPageStep(termBuffer_->getNumRows());
+
+ // FIXME: should not always move scroll bar to the bottom when
+ // buffer size is changed.
+ scrollBar_->setValue(termBuffer_->getNumLines() - termBuffer_->getNumRows());
+
+ bufferStart_ = scrollBar_->value();
+ bufferEnd_ = scrollBar_->value() + termBuffer_->getNumRows() - 1;
+
+ // notify session
+ session_->setScreenStart(bufferStart_);
+
+ FQ_VERIFY(connect(scrollBar_, SIGNAL(valueChanged(int)),
+ this, SLOT(scrollChanged(int))));
+
+ delete [] areLinesBlink_;
+ areLinesBlink_ = new bool[bufferEnd_ - bufferStart_ + 1];
+
+ scrollLine(0);
+}
+
+void FQTermScreen::termSizeChanged(int column, int row) {
+ FQ_TRACE("term", 3) << "The term size is changed to " << column << "x" << row;
+ syncBufferAndScreen();
+ bufferSizeChanged();
+ this->setPaintState(Repaint);
+ this->repaint();
+}
+
+/* ------------------------------------------------------------------------ */
+/* */
+/* Display */
+/* */
+/* ------------------------------------------------------------------------ */
+
+//set pixmap background
+
+void FQTermScreen::updateBackgroundPixmap() {
+ if (!hasBackground_)
+ return;
+
+ switch (backgroundRenderOption_) {
+ case 0:
+ //tile
+ {
+ backgroundPixmap_ = QPixmap(clientRectangle_.size());
+ QPainter painter(&backgroundPixmap_);
+ painter.drawTiledPixmap(backgroundPixmap_.rect(), originBackgroundPixmap_);
+ }
+ break;
+ case 1:
+ //center
+ {
+ backgroundPixmap_ = QPixmap(clientRectangle_.size());
+ backgroundPixmap_.fill(colors_[0]);
+ QPainter painter(&backgroundPixmap_);
+ int x = (backgroundPixmap_.size() - originBackgroundPixmap_.size()).width() / 2;
+ int y = (backgroundPixmap_.size() - originBackgroundPixmap_.size()).height() / 2;
+ painter.drawPixmap(x, y, originBackgroundPixmap_);
+ }
+ break;
+ case 2:
+ //stretch
+ backgroundPixmap_ = originBackgroundPixmap_.scaled(clientRectangle_.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ break;
+ }
+ /*
+ hasBackground_ = false;
+ backgroundPixmap_ = pixmap;
+ backgroundPixmapType_ = nType;
+ QPalette palette;
+
+ switch (nType) {
+ case 0:
+ // none
+ palette.setColor(backgroundRole(), colors_[0]);
+ setPalette(palette);
+ break;
+ case 1:
+ //{ // transparent{}
+ break;
+ case 2:
+ // tile
+ if (!pixmap.isNull())
+ palette.setBrush(backgroundRole(), QBrush(pixmap));
+ setPalette(palette);
+ // updateBackgroundPixmap( pixmap );
+ hasBackground_ = true;
+ break;
+ //}
+ case 3:
+ // center
+ if (!pixmap.isNull()) {
+ QPixmap pxmBg = QPixmap(size());
+ QPainter painter(&pxmBg);
+ pxmBg.fill(colors_[0]);
+ painter.drawPixmap((size().width() - pixmap.width()) / 2,
+ (size().height() - pixmap.height()) / 2,
+ pixmap.width(), pixmap.height(), pixmap);
+ palette.setBrush(backgroundRole(), QBrush(pxmBg));
+ setPalette(palette);
+ // updateBackgroundPixmap(pxmBg);
+ hasBackground_ = true;
+ break;
+ }
+ case 4:
+ // stretch
+ if (!pixmap.isNull()) {
+ float sx = (float)size().width() / pixmap.width();
+ float sy = (float)size().height() / pixmap.height();
+ QMatrix matrix;
+ matrix.scale(sx, sy);
+ palette.setBrush(backgroundRole(), QBrush(pixmap.transformed(matrix)));
+ setPalette(palette);
+ // updateBackgroundPixmap(pixmap.transformed( matrix ));
+ hasBackground_ = true;
+ break;
+ }
+ default:
+ palette.setColor(backgroundRole(), colors_[0]);
+ setPalette(palette);
+ // setBackgroundColor( m_color[0] );
+
+ }
+ */
+}
+
+void FQTermScreen::cursorEvent() {
+ if (isBlinkCursor_) {
+ setPaintState(Cursor);
+ isCursorShown_ = !isCursorShown_;
+ update();
+ }
+}
+
+void FQTermScreen::blinkEvent() {
+ if (hasBlinkText_) {
+ isBlinkScreen_ = !isBlinkScreen_;
+ setPaintState(Blink);
+ update();
+ }
+}
+
+void FQTermScreen::paintEvent(QPaintEvent *pe) {
+ FQ_TRACE("screen", 8) << "paintEvent " << pe->rect().x() << "," << pe->rect().y() << " "
+ << pe->rect().width() << "x" << pe->rect().height();
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing, FQTermPref::getInstance()->openAntiAlias_);
+ painter.setRenderHint(QPainter::TextAntialiasing, FQTermPref::getInstance()->openAntiAlias_);
+ if (paintState_ == System) {
+ repaintScreen(pe, painter);
+ return;
+ }
+
+ if (testPaintState(Repaint)) {
+ FQ_TRACE("screen", 8) << "paintEvent " << "repaint";
+ repaintScreen(pe, painter);
+ clearPaintState(Repaint);
+ }
+
+ if (testPaintState(NewData)) {
+ if (termBuffer_->isLightBackgroundMode() != is_light_background_mode_) {
+ FQ_TRACE("screen", 8) << "paintEvent " << "new data repaint";
+ is_light_background_mode_ = termBuffer_->isLightBackgroundMode();
+ if (is_light_background_mode_) {
+ colors_[0] = param_->foregroundColor_;
+ colors_[7] = param_->backgroundColor_;
+ } else {
+ colors_[0] = param_->backgroundColor_;
+ colors_[7] = param_->foregroundColor_;
+ }
+ repaintScreen(pe, painter);
+ } else {
+ FQ_TRACE("screen", 8) << "paintEvent " << "new data refresh";
+ refreshScreen(painter);
+ }
+
+ clearPaintState(NewData);
+ }
+
+ if (testPaintState(Blink)) {
+ FQ_TRACE("screen", 8) << "paintEvent " << "blink";
+ blinkScreen(painter);
+ clearPaintState(Blink);
+ }
+
+ if (testPaintState(Cursor)) {
+ FQ_TRACE("screen", 8) << "paintEvent " << "cursor";
+ updateCursor(painter);
+ clearPaintState(Cursor);
+ }
+
+ if (testPaintState(Widget)) {
+ updateWidgetRect(painter);
+ clearPaintState(Widget);
+ }
+}
+
+void FQTermScreen::blinkScreen(QPainter &painter) {
+ painter.setBackground(QBrush(colors_[0]));
+
+ for (int index = bufferStart_; index <= bufferEnd_; index++) {
+ if (areLinesBlink_[index - bufferStart_]) {
+ const FQTermTextLine *pTextLine = termBuffer_->getTextLineInBuffer(index);
+ uint linelength = pTextLine->getWidth();
+ const unsigned char *attr = pTextLine->getAttributes();
+ for (uint i = 0; i < linelength; ++i) {
+ if (GETBLINK(attr[i])) {
+ int startx = i;
+ while (i < linelength && GETBLINK(attr[i])) {
+ ++i;
+ }
+ drawLine(painter, index, startx, i, false);
+ }
+ }
+ }
+ }
+}
+
+void FQTermScreen::updateCursor(QPainter &painter) {
+ if (termBuffer_->getCaretColumn() >= termBuffer_->getNumColumns()) {
+ // we only allow the cursor located beyond the terminal screen temporarily.
+ // just ignore this.
+ return;
+ }
+
+ bool isCursorShown = isCursorShown_;
+ if (FQTermPref::getInstance()->isBossColor_) {
+ isCursorShown = true;
+ }
+
+ if (termBuffer_->getCaretLine() <= bufferEnd_
+ && termBuffer_->getCaretLine() >= bufferStart_) {
+
+ if (!isCursorShown) {
+ const FQTermTextLine *pTextLine
+ = termBuffer_->getTextLineInBuffer(termBuffer_->getCaretLine());
+ if ((int)pTextLine->getWidth() > termBuffer_->getCaretColumn()) {
+ int startx = pTextLine->getCellBegin(termBuffer_->getCaretColumn());
+ int endx = pTextLine->getCellEnd(termBuffer_->getCaretColumn() + 1);
+ if ((int)pTextLine->getWidth() > endx) {
+ endx = pTextLine->getCellEnd(endx + 1);
+ }
+ drawLine(painter, termBuffer_->getCaretLine(), startx, endx,
+ termBuffer_->getCaretLine());
+ } else {
+ //painter.fillRect(mapToRect(termBuffer_->getCaretColumn(), termBuffer_->getCaretLine(), 1, 1),colors_[0]);
+ drawBackground(painter, mapToRect(termBuffer_->getCaretColumn(),termBuffer_->getCaretLine(), 1, 1), 0);
+ }
+ return;
+ }
+
+ QPoint pt = mapToPixel(QPoint(termBuffer_->getCaretColumn(),
+ termBuffer_->getCaretLine()));
+ switch (param_->cursorType_) {
+ case 0:
+ // block
+ {
+ int x = termBuffer_->getCaretColumn();
+ int y = termBuffer_->getCaretLine();
+ const FQTermTextLine *pTextLine = termBuffer_->getTextLineInBuffer(y);
+ if ((int)pTextLine->getWidth() > x) {
+
+ int startx = pTextLine->getCellBegin(x);
+ int endx = pTextLine->getCellEnd(x + 1);
+
+ drawLine(painter, termBuffer_->getCaretLine(), startx, endx,
+ termBuffer_->getCaretLine());
+
+ QRect rect = mapToRect(startx, termBuffer_->getCaretLine(), 1, 1);
+ rect.translate(0, 0);
+ painter.fillRect(rect, colors_[7]);
+ } else {
+ QRect rect = mapToRect(termBuffer_->getCaretColumn(),
+ termBuffer_->getCaretLine(), 1, 1);
+ rect.translate(0, 0);
+ painter.fillRect(rect,
+ colors_[7]);
+ }
+ }
+ break;
+ case 1:
+ // underline
+ painter.fillRect(pt.x(), pt.y() + 9 * charHeight_ / 10, charWidth_,
+ charHeight_ / 10, colors_[7]);
+ break;
+ case 2:
+ // I type
+ painter.fillRect(pt.x(), pt.y() + 1, charWidth_ / 5, charHeight_ - 1,
+ colors_[7]);
+ break;
+ default:
+ painter.fillRect(pt.x(), pt.y()+1,
+ charWidth_, charHeight_ - 1, colors_[7]);
+ }
+ }
+}
+
+
+// refresh the screen when
+// 1. received new contents form server
+// 2. scrolled by user
+void FQTermScreen::refreshScreen(QPainter &painter) {
+ FQ_FUNC_TIMER("screen_paint", 5);
+
+ if (cursorTimer_->isActive()) {
+ cursorTimer_->stop();
+ }
+
+ if (blinkTimer_->isActive()) {
+ blinkTimer_->stop();
+ }
+
+ hasBlinkText_ = false;
+ isCursorShown_ = true;
+
+ for (int index = bufferStart_; index <= bufferEnd_; index++) {
+
+ FQ_VERIFY(index < termBuffer_->getNumLines());
+
+ const FQTermTextLine *pTextLine = termBuffer_->getTextLineInBuffer(index);
+
+ if (pTextLine->hasBlink()) {
+ hasBlinkText_ = true;
+ areLinesBlink_[index - bufferStart_] = true;
+ } else {
+ areLinesBlink_[index - bufferStart_] = false;
+ }
+
+ unsigned int startx, endx;
+ if (!pTextLine->getDirtyCells(startx, endx)) {
+ continue;
+ }
+
+ /*
+ Finally get around this for pku & ytht, don't know why some weird things
+ happened when only erase and draw the changed part.
+ */
+ startx = pTextLine->getCellBegin(startx);
+
+ int len = -1;
+ if ((int)endx != -1) {
+ len = endx - startx;
+ } else {
+ len = pTextLine->getMaxCellCount() - startx;
+ }
+
+ QRect rect = mapToRect(startx, index, len, 1);
+
+ //painter.fillRect(rect ,QBrush(colors_[0]));
+ drawBackground(painter, rect, 0);
+
+ drawLine(painter, index, startx, endx);
+
+ session_->clearLineChanged(index);
+ }
+
+ updateMicroFocus();
+ if (termWindow_->isConnected()) {
+ updateCursor(painter);
+ }
+
+
+ if (termWindow_->isConnected()) {
+ cursorTimer_->start(1000);
+ }
+
+ if (hasBlinkText_) {
+ blinkTimer_->start(1000);
+ }
+}
+
+void FQTermScreen::repaintScreen(QPaintEvent *pe, QPainter &painter) {
+ FQ_FUNC_TIMER("screen_paint", 5);
+
+ //painter.setBackground(QBrush(colors_[0]));
+
+ FQ_TRACE("screen", 5) << "Client area: " << pe->rect().width()
+ << "x" << pe->rect().height();
+
+ QRect rect = pe->rect().intersect(clientRectangle_);
+ //painter.eraseRect(rect);
+ drawBackground(painter, rect, 0);
+ QPoint tlPoint = mapToChar(QPoint(rect.left(), rect.top()));
+
+ QPoint brPoint = mapToChar(QPoint(rect.right(), rect.bottom()));
+
+ for (int y = tlPoint.y(); y <= brPoint.y(); y++) {
+ drawLine(painter, y);
+ }
+ if (termWindow_->getUrlStartPoint() != termWindow_->getUrlEndPoint()) {
+ drawUnderLine(painter, termWindow_->getUrlStartPoint(), termWindow_->getUrlEndPoint());
+ }
+
+
+}
+
+/////////////////////////////////////////////////
+//TODO: change to a more powerful function
+FQTermScreen::TextRenderingType FQTermScreen::charRenderingType(const QChar& c) {
+ if (c == ' ') return HalfAndSpace;
+ if (c.unicode() > 0x7f && c.unicode() <= 0x2dff) {
+ return FullAndAlign;
+ } else if (c.unicode() > 0x2dff) {
+ if (cnFixedPitch_)
+ return FullNotAlign;
+ else
+ return FullAndAlign;
+ }
+ return HalfAndAlign;
+}
+
+static bool isColorBlock(const QChar& c) {
+ if (!FQTermPref::getInstance()->isAnsciiEnhance_) return false;
+ return ((c.unicode() >= 0x2581 && c.unicode() <= 0x258f) ||
+ (c.unicode() >= 0x25e2 && c.unicode() <= 0x25e5));
+}
+
+/////////////////////////////////////////////////
+// draw a line with the specialAtter if given.
+// modified by hooey to draw part of the line.
+void FQTermScreen::drawLine(QPainter &painter, int index, int startx, int endx,
+ bool complete) {
+ FQ_ASSERT(index < termBuffer_->getNumLines());
+
+ const FQTermTextLine *pTextLine = termBuffer_->getTextLineInBuffer(index);
+ const unsigned char *color = pTextLine->getColors();
+ const unsigned char *attr = pTextLine->getAttributes();
+
+ uint linelength = pTextLine->getWidth();
+ bool isSessionSelected = session_->isSelectedMenu(index);
+ bool isTransparent = isSessionSelected && param_->menuType_ == 2;
+
+ menuRect_ = QRect();
+ QString cstrText;
+
+ if (startx < 0) {
+ startx = 0;
+ }
+
+ if (endx > (long)linelength || endx < 0) {
+ endx = linelength;
+ }
+
+ if (complete == true && isSessionSelected) {
+ menuRect_ = drawMenuSelect(painter, index);
+ startx = 0;
+ endx = linelength;
+ }
+
+ startx = pTextLine->getCellBegin(startx);
+ endx = pTextLine->getCellEnd(endx);
+
+ bool isMonoSpace = true;
+ if (!cnFixedPitch_
+ || !enFixedPitch_) {
+ isMonoSpace = false;
+ }
+
+
+
+ for (unsigned i = startx; (long)i < endx;) {
+ startx = i;
+ unsigned char tempcp = color[i];
+ unsigned char tempea = attr[i];
+ unsigned cell_begin = pTextLine->getCellBegin(startx);
+ unsigned cell_end = pTextLine->getCellEnd(cell_begin + 1);
+ TextRenderingType temprt;
+ cstrText.clear();
+ pTextLine->getPlainText(cell_begin, cell_end, cstrText);
+ temprt = charRenderingType(cstrText[0]);
+ bool color_block = isColorBlock(cstrText[0]);
+
+ bool bSelected = termBuffer_->isSelected(
+ QPoint(i, index), param_->isRectSelect_);
+
+ // get str of the same attribute
+ while ((long)i < endx && tempcp == color[i] && tempea == attr[i] &&
+ bSelected == termBuffer_->isSelected(
+ QPoint(i, index), param_->isRectSelect_)) {
+ unsigned cellBegin = pTextLine->getCellBegin(i);
+ unsigned cellEnd = pTextLine->getCellEnd(cellBegin + 1);
+ cstrText.clear();
+ pTextLine->getPlainText(cellBegin, cellEnd, cstrText);
+ if (temprt != charRenderingType(cstrText[0])) {
+ break;
+ }
+ bool colorBlock = isColorBlock(cstrText[0]);
+ if (cell_end != cellEnd && colorBlock || color_block && !colorBlock) {
+ break;
+ }
+ ++i;
+ }
+
+ cell_end = pTextLine->getCellEnd(i);
+ cstrText.clear();
+ pTextLine->getPlainText(cell_begin, cell_end, cstrText);
+
+ unsigned j = i;
+ if (j > 0 && pTextLine->getCellBegin(j) == pTextLine->getCellBegin(j - 1)
+ && *(pTextLine->getColors() + j) != *(pTextLine->getColors() + j - 1)) {
+ j++;
+ }
+ for (; j < cell_end; ++j) {
+ cstrText.append((QChar)URC);
+ }
+
+ int text_width = cell_end - cell_begin;
+
+ bool draw_half_begin_char = false;
+
+ if (startx > 0 && pTextLine->getCellBegin(startx) == pTextLine->getCellBegin(startx - 1)
+ && *(pTextLine->getColors() + startx) != *(pTextLine->getColors() + startx - 1)) {
+//#if defined(WIN32)
+ draw_half_begin_char = true;
+//#else
+// startx++;
+//#endif
+ }
+
+ painter.setFont((temprt == HalfAndAlign || temprt == HalfAndSpace)?*englishFont_:*nonEnglishFont_);
+ if (isMonoSpace && temprt != FullAndAlign) {
+ QFont& font = (temprt == HalfAndAlign || temprt == HalfAndSpace)?*englishFont_:*nonEnglishFont_;
+ if (temprt == HalfAndAlign) {
+ font.setLetterSpacing(QFont::AbsoluteSpacing, enLetterSpacing_);
+ } else if (temprt == HalfAndSpace) {
+ font.setLetterSpacing(QFont::AbsoluteSpacing, spLetterSpacing_);
+ } else {
+ font.setLetterSpacing(QFont::AbsoluteSpacing, cnLetterSpacing_);
+ }
+ painter.setFont(font);
+ if (draw_half_begin_char) {
+ text_width--;
+ draw_half_begin_char = false;
+ }
+ drawStr(painter, cstrText, startx, index, text_width,
+ tempcp, tempea, isTransparent, bSelected);
+ font.setLetterSpacing(QFont::AbsoluteSpacing, 0.0);
+ } else if (temprt == FullNotAlign) {
+ nonEnglishFont_->setLetterSpacing(QFont::AbsoluteSpacing, cnLetterSpacing_);
+ painter.setFont(*nonEnglishFont_);
+ if (draw_half_begin_char) {
+ text_width--;
+ draw_half_begin_char = false;
+ }
+ drawStr(painter, cstrText, startx, index, text_width,
+ tempcp, tempea, isTransparent, bSelected);
+ nonEnglishFont_->setLetterSpacing(QFont::AbsoluteSpacing, 0.0);
+ } else if (temprt == HalfAndSpace) {
+ englishFont_->setLetterSpacing(QFont::AbsoluteSpacing, spLetterSpacing_);
+ painter.setFont(*englishFont_);
+ if (draw_half_begin_char) {
+ text_width--;
+ draw_half_begin_char = false;
+ }
+ drawStr(painter, cstrText, startx, index, text_width,
+ tempcp, tempea, isTransparent, bSelected);
+ englishFont_->setLetterSpacing(QFont::AbsoluteSpacing, 0.0);
+ }else {
+ englishFont_->setLetterSpacing(QFont::AbsoluteSpacing, enLetterSpacing_);
+ nonEnglishFont_->setLetterSpacing(QFont::AbsoluteSpacing, cnLetterSpacing_);
+ if (temprt == HalfAndAlign)
+ painter.setFont(*englishFont_);
+ else if (temprt == FullAndAlign)
+ painter.setFont(*nonEnglishFont_);
+ // Draw Characters one by one to fix the variable-width font
+ // display problem.
+ int offset = 0;
+ for (uint j = 0; (long)j < cstrText.length(); ++j) {
+ // TODO_UTF16: wrong character width here.
+ if (temprt == HalfAndAlign) {
+ drawStr(painter, (QString)cstrText.at(j),
+ startx + offset, index, 1,
+ tempcp, tempea,
+ isTransparent, bSelected);
+ offset++;
+ } else if (temprt == FullAndAlign){
+
+
+ int w = 2;
+ if (draw_half_begin_char) {
+ w--;
+ }
+ drawStr(painter, (QString)cstrText.at(j),
+ startx + offset, index, w,
+ tempcp, tempea,
+ isTransparent, bSelected);
+ if (draw_half_begin_char) {
+ draw_half_begin_char = false;
+ offset--;
+ }
+ offset += 2;
+
+ }
+ }
+ nonEnglishFont_->setLetterSpacing(QFont::AbsoluteSpacing, 0.0);
+ englishFont_->setLetterSpacing(QFont::AbsoluteSpacing, 0.0);
+ }
+ }
+}
+//
+// draw functions
+void FQTermScreen::drawStr(QPainter &painter, const QString &str,
+ int x, int y, int length,
+ unsigned char color, unsigned char attr,
+ bool transparent, bool selected) {
+ unsigned char cp = color;
+ unsigned char ea = attr;
+
+ // test bold mask or always highlighted
+ if (GETBRIGHT(ea) || param_->isAlwaysHighlight_) {
+ cp |= SET_FG_HIGHLIGHT(1);
+ }
+
+ // use 8-15 color
+ // test dim mask
+ if (GETDIM(ea)) {
+ }
+
+ // test underline mask
+ if (GETUNDERLINE(ea)) {
+ QFont font = painter.font();
+ font.setUnderline(true);
+ painter.setFont(font);
+ } else {
+ QFont font = painter.font();
+ font.setUnderline(false);
+ painter.setFont(font);
+ }
+ // test blink mask
+ if (GETBLINK(ea)){}
+ // test rapidblink mask
+ if (GETRAPIDBLINK(ea)){}
+ // test reverse mask
+ if (GETREVERSE(ea)) {
+ cp = GET_INVERSE_COLOR(cp);
+ }
+
+ if (selected) {
+ cp = GET_INVERSE_COLOR(cp);
+ }
+
+ // test invisible mask
+ if (GETINVISIBLE(ea)) {
+ }
+
+
+
+ int pen_color_index = GETFG(cp);
+ if (!param_->isAnsiColor_) {
+ if (GETBG(cp) != 0) {
+ pen_color_index = 0;
+ } else {
+ pen_color_index = 7;
+ }
+ }
+ painter.setPen(colors_[pen_color_index]);
+
+ int brush_color_index = GETBG(cp);
+ if (!param_->isAnsiColor_) {
+ if (GETBG(cp) != 0) {
+ brush_color_index = 7;
+ } else {
+ brush_color_index = 0;
+ }
+ }
+
+
+ QBrush brush(colors_[brush_color_index]);
+
+ // black on white without attr
+ if (FQTermPref::getInstance()->isBossColor_) {
+ painter.setPen(Qt::black);
+ brush = QBrush(Qt::white);
+ }
+
+ QPoint pt = mapToPixel(QPoint(x, y));
+ QRect rcErase = mapToRect(x, y, length, 1);
+
+ if (!menuRect_.intersects(rcErase)){
+ if (x == 0) {
+ rcErase.setLeft(rcErase.left() - 1);
+ }
+ //painter.fillRect(rcErase, brush);
+ drawBackground(painter, rcErase, brush_color_index);
+ if (x == 0) {
+ rcErase.setLeft(rcErase.left() + 1);
+ }
+ } else {
+ QRect rcKeep = menuRect_.intersected(rcErase);
+ rcKeep.setY(rcErase.y());
+ rcKeep.setHeight(rcErase.height());
+ if (rcErase.left() < rcKeep.left()) {
+ QPoint point = rcKeep.bottomLeft();
+ point.setX(point.x() + 1);
+ point.setY(point.y() + 1);
+ painter.fillRect(QRect(rcErase.topLeft(), point),
+ brush);
+ }
+ if (rcKeep.right() < rcErase.right()) {
+ QPoint point = rcErase.bottomRight();
+ point.setX(point.x() + 1);
+ point.setY(point.y() + 1);
+
+ painter.fillRect(QRect(rcKeep.topRight(), point),
+ brush);
+ }
+ }
+
+ if (!(isBlinkScreen_ && GETBLINK(attr))) {
+ FQ_TRACE("draw_text", 10) << "draw text: " << str;
+
+ QFontMetrics qm = painter.fontMetrics();
+ int font_height = qm.height();
+ int expected_font_height = rcErase.height();
+ int height_gap = expected_font_height - font_height;
+ int offset = (height_gap + 1)/2;
+ int ascent = qm.ascent() + offset;
+
+//#if defined(WIN32)
+ int verticalAlign = Qt::AlignVCenter;
+ switch(param_->alignMode_) {
+ case 0:
+ verticalAlign = Qt::AlignVCenter;
+ break;
+ case 1:
+ verticalAlign = Qt::AlignBottom;
+ break;
+ case 2:
+ verticalAlign = Qt::AlignTop;
+ break;
+ }
+ if (length > 2 || !isColorBlock(str[0])) {
+ painter.drawText(rcErase, Qt::AlignRight | verticalAlign, str);
+ } else {
+ //here length == 1 or == 2, isColorBlock == true, ensured in drawLine.
+ //when length == 1, we are drawing the second half of the block,
+ //otherwise the whole block.
+ //(c.unicode() >= 0x2581 && c.unicode() <= 0x258f)
+ //7 rectangles from shorter to taller (1/8 - 7/8 on the bottom)
+ //1 rectangle full
+ //7 rectangles from thicker to thinner (7/8 - 1/8 on the left)
+ //(c.unicode() >= 0x25e2 && c.unicode() <= 0x25e5)
+ //4 triangles bottom-right, bottom-left, top-left, top-right
+ QPolygon bound = rcErase;
+ QPolygon toDraw;
+ QRect rcFull = mapToRect(x, y, 2, 1);
+ rcFull.setBottom(rcFull.bottom() + 1);
+ rcFull.setRight(rcFull.right() + 1);
+ int unic = str[0].unicode();
+ if (unic >= 0x2581 && unic <= 0x2588) {
+ toDraw = QRect(rcFull.left(), rcFull.top() - rcFull.height() * (unic - 0x2581 + 1 - 8) / 8, rcFull.width(), rcFull.height() * (unic - 0x2581 + 1) / 8);
+ } else if (unic > 0x2588 && unic <= 0x258f) {
+ toDraw = QRect(rcFull.left(), rcFull.top(), rcFull.width() * (8 - unic + 0x2589 - 1) / 8, rcFull.height());
+ } else { //triangles
+ QVector<QPoint> points;
+ points.push_back(rcFull.topLeft());
+ points.push_back(rcFull.topRight());
+ points.push_back(rcFull.bottomRight());
+ points.push_back(rcFull.bottomLeft());
+ points.remove(unic - 0x25e2);
+ toDraw = points;
+ }
+ QBrush oldBrush = painter.brush();
+ painter.setBrush(colors_[pen_color_index]);
+ QPen oldPen = painter.pen();
+ painter.setPen(Qt::transparent);
+ painter.drawConvexPolygon(toDraw.intersected(bound));
+ painter.setBrush(oldBrush);
+ painter.setPen(oldPen);
+ }
+//#else
+// painter.drawText(pt.x(), pt.y() + ascent, str);
+//#endif
+ }
+}
+
+
+void FQTermScreen::eraseRect(QPainter &, int, int, int, int, short){
+ FQ_VERIFY(false);
+}
+
+void FQTermScreen::bossColor() {
+ if (FQTermPref::getInstance()->isBossColor_) {
+ colors_[0] = Qt::white;
+ colors_[7] = Qt::black;
+ QPalette palette;
+ palette.setColor(backgroundRole(), Qt::white);
+ setPalette(palette);
+
+ } else {
+ colors_[0] = param_->backgroundColor_;
+ colors_[7] = param_->foregroundColor_;
+ }
+
+
+ setPaintState(Repaint);
+ update();
+}
+
+QRect FQTermScreen::drawMenuSelect(QPainter &painter, int index) {
+ QRect rcSelect, rcMenu, rcInter;
+ // FIXME: what is this for
+/*
+ if (termBuffer_->isSelected(index)) {
+ bool is_rect_sel = param_->isRectSelect_;
+ rcSelect = mapToRect(termBuffer_->getSelectRect(index, is_rect_sel));
+ if (FQTermPref::getInstance()->isBossColor_) {
+ painter.fillRect(rcSelect, QBrush(colors_[0]));
+ } else {
+ painter.fillRect(rcSelect, QBrush(colors_[7]));
+ }
+ }
+*/
+ if (session_->isSelectedMenu(index)) {
+ rcMenu = mapToRect(session_->getMenuRect());
+ // m_pBand->setGeometry(rcMenu);
+ // m_pBand->show();
+ switch (param_->menuType_) {
+ case 0:
+ // underline
+ painter.fillRect(rcMenu.x(), rcMenu.y() + 10 * charHeight_ / 11,
+ rcMenu.width(), charHeight_ / 11, colors_[7]);
+ break;
+ case 2:
+ painter.fillRect(rcMenu, QBrush(FQTermPref::getInstance()->isBossColor_?colors_[0]:param_->menuColor_));
+ break;
+ }
+ }
+ return rcMenu;
+}
+
+/* ------------------------------------------------------------------------ */
+/* */
+/* Auxilluary */
+/* */
+/* ------------------------------------------------------------------------ */
+
+QSize FQTermScreen::getScreenSize() const
+{
+ return QSize(termBuffer_->getNumColumns() * charWidth_, termBuffer_->getNumRows() * charHeight_);
+}
+
+QPoint FQTermScreen::mapToPixel(const QPoint &point) {
+ int dx = (getScreenSize().width() - clientRectangle_.width()) * FQTermPref::getInstance()->displayOffset_ / 100;
+ int dy = getVerticalSetting();
+ QPoint pt = clientRectangle_.topLeft();
+ pt.setX(pt.x() - dx);
+ pt.setY(pt.y() - dy);
+ QPoint pxlPoint;
+
+ pxlPoint.setX(point.x() *charWidth_ + pt.x());
+ pxlPoint.setY((point.y() - bufferStart_) *charHeight_ + pt.y());
+
+ return pxlPoint;
+}
+// mapToChar: get a position in the window and return the
+// corresponding char position
+QPoint FQTermScreen::mapToChar(const QPoint &point) {
+ int dx = (getScreenSize().width() - clientRectangle_.width()) * FQTermPref::getInstance()->displayOffset_ / 100;
+ int dy = getVerticalSetting();
+ QPoint pt = clientRectangle_.topLeft();
+ pt.setX(pt.x() - dx);
+ pt.setY(pt.y() - dy);
+ QPoint chPoint;
+ chPoint.setX(qMin(qMax(0, int((point.x() - pt.x()) / charWidth_)),
+ termBuffer_->getNumColumns() - 1));
+ chPoint.setY(qMin(qMax(0, int((point.y() - pt.y()) / charHeight_) + bufferStart_),
+ bufferEnd_));
+
+ //FIXME add bound check
+ return chPoint;
+}
+
+QRect FQTermScreen::mapToRect(int x, int y, int width, int height) {
+ QPoint pt = mapToPixel(QPoint(x, y));
+
+ if (width == -1) {
+ // to the end
+ return QRect(pt.x(), pt.y(), getScreenSize().width(), charHeight_ *height);
+ } else {
+ return QRect(pt.x(), pt.y(), width *charWidth_, charHeight_ *height);
+ }
+}
+
+QRect FQTermScreen::mapToRect(const QRect &rect) {
+ return mapToRect(rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+// from KImageEffect::fade
+QImage &FQTermScreen::fade(QImage &img, float val, const QColor &color) {
+ if (img.width() == 0 || img.height() == 0) {
+ return img;
+ }
+
+ // We don't handle bitmaps
+ if (img.depth() == 1) {
+ return img;
+ }
+
+ unsigned char tbl[256];
+ for (int i = 0; i < 256; i++) {
+ tbl[i] = (int)(val *i + 0.5);
+ }
+
+ int red = color.red();
+ int green = color.green();
+ int blue = color.blue();
+
+ QRgb col;
+ int r, g, b, cr, cg, cb;
+
+ if (img.depth() <= 8) {
+ // pseudo color
+ for (int i = 0; i < img.numColors(); i++) {
+ col = img.color(i);
+ cr = qRed(col);
+ cg = qGreen(col);
+ cb = qBlue(col);
+ if (cr > red) {
+ r = cr - tbl[cr - red];
+ } else {
+ r = cr + tbl[red - cr];
+ }
+ if (cg > green) {
+ g = cg - tbl[cg - green];
+ } else {
+ g = cg + tbl[green - cg];
+ }
+ if (cb > blue) {
+ b = cb - tbl[cb - blue];
+ } else {
+ b = cb + tbl[blue - cb];
+ }
+ img.setColor(i, qRgba(r, g, b, qAlpha(col)));
+ }
+ } else {
+ // truecolor
+ for (int y = 0; y < img.height(); y++) {
+ QRgb *data = (QRgb*)img.scanLine(y);
+ for (int x = 0; x < img.width(); x++) {
+ col = *data;
+ cr = qRed(col);
+ cg = qGreen(col);
+ cb = qBlue(col);
+ if (cr > red) {
+ r = cr - tbl[cr - red];
+ } else {
+ r = cr + tbl[red - cr];
+ }
+ if (cg > green) {
+ g = cg - tbl[cg - green];
+ } else {
+ g = cg + tbl[green - cg];
+ }
+ if (cb > blue) {
+ b = cb - tbl[cb - blue];
+ } else {
+ b = cb + tbl[blue - cb];
+ }
+ *data++ = qRgba(r, g, b, qAlpha(col));
+ }
+ }
+ }
+
+ return img;
+}
+
+void FQTermScreen::inputMethodEvent(QInputMethodEvent *e) {
+ FQ_TRACE("ime", 5) << "Preedit: " << e->preeditString();
+ FQ_TRACE("ime", 5) << "Commit: " << e->commitString();
+
+ FQ_TRACE("ime", 8) << "Replace: " << e->replacementStart()
+ << ", " << e->replacementLength();
+
+ if (e->preeditString().size() > 0) {
+ nonEnglishFont_->setLetterSpacing(QFont::AbsoluteSpacing, 0.0);
+ preedit_line_->setPreeditText(e, nonEnglishFont_);
+ nonEnglishFont_->setLetterSpacing(QFont::AbsoluteSpacing, 0.0);
+ QPoint pt = mapToPixel(QPoint(termBuffer_->getCaretColumn(),
+ termBuffer_->getCaretLine()));
+ preedit_line_->move(pt);
+ preedit_line_->show();
+ preedit_line_->update();
+ } else {
+ preedit_line_->hide();
+ }
+
+ if (e->commitString().size() > 0) {
+ emit inputEvent(e->commitString());
+ }
+}
+
+QVariant FQTermScreen::inputMethodQuery(Qt::InputMethodQuery property) const {
+ FQ_TRACE("ime", 5) << "Query: " << property;
+
+ int row = termBuffer_->getCaretRow();
+ int col = termBuffer_->getCaretColumn();
+
+ switch (property) {
+ case Qt::ImMicroFocus:
+ // Here we must use (column + 1, row + 1) as the result,
+ // otherwise MS Pinyin or GPY won't work well.
+ return QVariant(
+ QRect((termBuffer_->getCaretColumn()) * charWidth_
+ + (preedit_line_->isVisible() ?
+ preedit_line_->getCaretOffset() : 0),
+ (termBuffer_->getCaretRow()) * charHeight_,
+ charWidth_, charHeight_));
+ case Qt::ImFont:
+ {
+ nonEnglishFont_->setLetterSpacing(QFont::AbsoluteSpacing, 0.0);
+ QVariant var(*nonEnglishFont_);
+ nonEnglishFont_->setLetterSpacing(QFont::AbsoluteSpacing, 0.0);
+ return var;
+ }
+ case Qt::ImCurrentSelection:
+ return QVariant(QString());
+ case Qt::ImCursorPosition:
+ case Qt::ImSurroundingText:
+ {
+ tmp_im_query_->clear();
+ const FQTermTextLine *line = termBuffer_->getTextLineInTerm(row);
+ line->getPlainText(0, line->getCellEnd(col), *tmp_im_query_);
+
+ // TODO_UTF16: here only BMP is considered.
+ if (Qt::ImCursorPosition) {
+ return QVariant(tmp_im_query_->size());
+ } else {
+ tmp_im_query_->clear();
+ line->getPlainText(0, line->getWidth(), *tmp_im_query_);
+ return QVariant(QString(*tmp_im_query_));
+ }
+ }
+ break;
+ default:
+ return QVariant();
+ }
+}
+
+void FQTermScreen::updateFixedPitchInfo() {
+ QFont cnFont(*nonEnglishFont_);
+ QFont enFont(*englishFont_);
+ int cnPixelSize = nonEnglishFont_->pixelSize() <= 5 ? 12 : nonEnglishFont_->pixelSize();
+ int enPixelSize = englishFont_->pixelSize() <= 5 ? 12 : englishFont_->pixelSize();
+ cnFont.setPixelSize(cnPixelSize);
+ enFont.setPixelSize(enPixelSize);
+ QString cnTestString = QString::fromUtf8("\xe5\x9c\xb0\xe6\x96\xb9\xe6\x94\xbf\xe5\xba\x9c");
+ cnFixedPitch_ = (QFontMetrics(cnFont).width(cnTestString) ==
+ cnTestString.length() * QFontMetrics(cnFont).width(cnTestString.at(0)));
+ QString enTestString = QString::fromUtf8("www.newsmth.net");
+ enFixedPitch_ = QFontInfo(enFont).fixedPitch() &&
+ (QFontMetrics(enFont).width(enTestString) == enTestString.length() * QFontMetrics(enFont).width(enTestString.at(0)));
+
+ FQ_TRACE("font", 10) << "\nenglish: " << enFixedPitch_
+ << "\n chinese: " << cnFixedPitch_;
+
+}
+
+void FQTermScreen::setTermFont(bool isEnglish, const QFont& font)
+{
+ QFont *&pFont = isEnglish?englishFont_:nonEnglishFont_;
+ delete pFont;
+ if (param_->isFontAutoFit_ == 1) {
+ pFont = new QFont(font.family());
+ pFont->setKerning(false);
+ } else {
+ pFont = new QFont(font);
+ pFont->setKerning(false);
+ setFontMetrics();
+ }
+ updateFixedPitchInfo();
+ emit termFontChange(isEnglish, font);
+}
+
+QFont FQTermScreen::termFont(bool isEnglish) {
+ return isEnglish?(*englishFont_):(*nonEnglishFont_);
+}
+
+void FQTermScreen::drawUnderLine(QPainter &painter, const QPoint& startPoint, const QPoint& endPoint) {
+ if (startPoint.y() == endPoint.y()) {
+ drawSingleUnderLine(painter, startPoint, endPoint);
+ } else {
+ drawSingleUnderLine(painter, startPoint, QPoint(termBuffer_->getNumColumns(), startPoint.y()));
+ drawSingleUnderLine(painter, QPoint(0, endPoint.y()), endPoint);
+ for (int i = startPoint.y() + 1; i < endPoint.y(); ++i) {
+ drawSingleUnderLine(painter, QPoint(0, i), QPoint(termBuffer_->getNumColumns(), i));
+ }
+ }
+}
+
+void FQTermScreen::drawSingleUnderLine(QPainter &painter, const QPoint& startPoint, const QPoint& endPoint) {
+ FQ_VERIFY(startPoint.y() == endPoint.y());
+ QPoint realStart = mapToPixel(startPoint);
+ QPoint realEnd = mapToPixel(endPoint);
+ painter.fillRect(realStart.x(), realStart.y() + 10 * charHeight_ / 11,
+ realEnd.x() - realStart.x(), charHeight_ / 11, FQTermPref::getInstance()->isBossColor_?colors_[0]:param_->menuColor_);
+}
+
+void FQTermScreen::setFontAntiAliasing( bool on /*= true*/ ) {
+ englishFont_->setStyleStrategy(on?QFont::PreferAntialias:QFont::NoAntialias);
+ nonEnglishFont_->setStyleStrategy(on?QFont::PreferAntialias:QFont::NoAntialias);
+}
+
+void FQTermScreen::widgetHideAt(const QRect& rect){
+ widgetRect_ = rect;
+ setPaintState(Widget);
+ update();
+}
+
+void FQTermScreen::updateWidgetRect(QPainter& painter) {
+
+
+
+ QRect rect = widgetRect_.intersect(clientRectangle_);
+ if (rect.isEmpty())
+ return;
+ drawBackground(painter, rect, 0);
+
+ QPoint tlPoint = mapToChar(QPoint(rect.left(), rect.top()));
+
+ QPoint brPoint = mapToChar(QPoint(rect.right(), rect.bottom()));
+
+ for (int y = tlPoint.y(); y <= brPoint.y(); y++) {
+ const FQTermTextLine *pTextLine = termBuffer_->getTextLineInBuffer(y);
+ int startx = qMin(tlPoint.x(),int(pTextLine->getWidth()));
+ int endx = qMin(brPoint.x(), int(pTextLine->getWidth()));
+ startx = pTextLine->getCellBegin(startx);
+ endx = pTextLine->getCellEnd(endx);
+ drawLine(painter, y, startx, endx);
+ }
+ widgetRect_ = QRect();
+}
+
+void FQTermScreen::drawBackgroundPixmap(QPainter& painter, const QRect& rect) {
+ if (!hasBackground_ ||
+ FQTermPref::getInstance()->isBossColor_ ||
+ backgroundPixmap_.isNull()) {
+ return;
+ }
+ if (backgroundCoverage_ == 1) { //Only padding, we do not draw screen background.
+ QRegion clip = rect;
+ QRect screenRect = mapToRect(0, termBuffer_->getNumLines() - termBuffer_->getNumRows(), termBuffer_->getNumColumns(), termBuffer_->getNumRows());
+ clip -= screenRect;
+ QVector<QRect> rects = clip.rects();
+ foreach(QRect r, rects) {
+ painter.drawPixmap(r, backgroundPixmap_, r);
+ }
+ return;
+ }
+ painter.drawPixmap(rect, backgroundPixmap_, rect);
+}
+
+int FQTermScreen::getVerticalSetting() const {
+ int dy = -1;
+ switch (FQTermPref::getInstance()->vsetting_)
+ {
+ case 0:
+ {
+ // Top
+ dy = 0;
+ break;
+ }
+ case 1:
+ {
+ // Middle
+ dy = (getScreenSize().height() - clientRectangle_.height()) / 2;
+ break;
+ }
+ case 2:
+ {
+ // Bottom
+ dy = (getScreenSize().height() - clientRectangle_.height());
+ break;
+ }
+ default:
+ {
+ // The logic should never get here
+ break;
+ }
+ }
+ return dy;
+}
+
+void FQTermScreen::drawBackground(QPainter& painter, const QRect& rect, int colorIndex) {
+
+ if (FQTermPref::getInstance()->isBossColor_) {
+ painter.fillRect(rect, Qt::white);
+ return;
+ }
+ if (hasBackground_ && !backgroundPixmap_.isNull()) {
+ if (colorIndex == 0 || (backgroundCoverage_ != 1 && backgroundUseAlpha_))
+ drawBackgroundPixmap(painter, rect);
+ if (backgroundCoverage_ == 1) { //Background only padding, we draw screen background.
+ QRegion clip = rect;
+ QRect screenRect = mapToRect(0, termBuffer_->getNumLines() - termBuffer_->getNumRows(), termBuffer_->getNumColumns(), termBuffer_->getNumRows());
+ clip &= screenRect;
+ QVector<QRect> rects = clip.rects();
+ foreach(QRect r, rects) {
+ painter.fillRect(r, colors_[colorIndex]);
+ }
+ } else {
+ if (backgroundUseAlpha_)
+ painter.setOpacity(double(backgroundAlpha_) / 100);
+ if (colorIndex != 0 || backgroundUseAlpha_)
+ painter.fillRect(rect, colors_[colorIndex]);
+ painter.setOpacity(1.0);
+ }
+ return;
+ }
+ painter.fillRect(rect, colors_[colorIndex]);
+}
+PreeditLine::PreeditLine(QWidget *parent,const QColor *colors)
+ : QWidget(parent),
+ pixmap_(new QPixmap()),
+ colors_(colors) {
+ //setFocusPolicy(Qt::NoFocus);
+ hide();
+}
+
+PreeditLine::~PreeditLine() {
+ delete pixmap_;
+}
+
+void PreeditLine::setPreeditText(QInputMethodEvent *e, const QFont *font) {
+ QString text = e->preeditString();
+
+ const QColor &white_color = colors_[7];
+ const QColor &black_color = colors_[0];
+
+ // MS Pinyin will add a white space '\u3000' in the end. Remove it.
+ text = text.trimmed();
+
+ QFontMetrics fm(*font);
+ QRect textRect = fm.boundingRect(text);
+
+ int width = textRect.width();
+ int height = textRect.height();
+
+ delete pixmap_;
+ pixmap_ = new QPixmap(width, height + 1); // add one pixel to shadow
+ // the original cursor on
+ // screen.
+ this->resize(width, height + 1);
+
+ QPainter painter(pixmap_);
+ painter.fillRect(textRect, QBrush(black_color));
+
+ painter.setFont(*font);
+ painter.setBackgroundMode(Qt::OpaqueMode);
+
+ // Parse attributes of the input method event.
+ for (int i = 0; i < e->attributes().size(); ++i) {
+ const QInputMethodEvent::Attribute &a = e->attributes().at(i);
+ switch(a.type) {
+ case QInputMethodEvent::Cursor:
+ FQ_TRACE("ime", 10) << "Cursor: " << a.start << ", " << a.length;
+ {
+ painter.setPen(white_color);
+
+ int caret_begin = fm.boundingRect(text.left(a.start)).right();
+
+ painter.drawLine(caret_begin, 0, caret_begin, height);
+ painter.drawLine(caret_begin + 1, 0, caret_begin + 1, height);
+
+ caret_offset_ = caret_begin;
+ }
+ break;
+ case QInputMethodEvent::TextFormat:
+ {
+ if (a.start + a.length > text.size()) {
+ // index over flow. ignore it.
+ break;
+ }
+
+ const QString &sub_text = text.mid(a.start, a.length);
+
+ int x = fm.boundingRect(text.left(a.start)).width();
+
+ QTextCharFormat f =
+ qvariant_cast<QTextFormat>(a.value).toCharFormat();
+
+ // Hack here.
+ // If the background() is of style Qt::SolidPattern,
+ // we consider this segment of text is highlighted.
+ // Otherwise it's normal preedit text.
+ if (f.background().style() == Qt::SolidPattern) {
+ QBrush bg(white_color);
+ bg.setStyle(Qt::SolidPattern);
+
+ painter.setBackground(bg);
+ painter.setPen(black_color);
+ } else {
+ QBrush bg(black_color);
+ bg.setStyle(Qt::SolidPattern);
+
+ painter.setBackground(bg);
+ painter.setPen(white_color);
+ }
+
+ // Draw the text;
+ painter.drawText(x, fm.ascent(), sub_text);
+
+ FQ_TRACE("ime", 10) << "Sub-text: " << sub_text;
+ FQ_TRACE("ime", 10) << "Format: " << a.start << ", " << a.length;
+ FQ_TRACE("ime", 10) << "Background: " << f.background();
+ FQ_TRACE("ime", 10) << "Pen: " << f.textOutline();
+ }
+ break;
+ case QInputMethodEvent::Language:
+ FQ_TRACE("ime", 10) << "Language: " << a.start << ", " << a.length;
+ break;
+ case QInputMethodEvent::Ruby:
+ FQ_TRACE("ime", 10) << "Ruby: " << a.start << ", " << a.length;
+ break;
+ }
+ }
+
+ // Draw a line under the preedit text.
+ QPen pen(white_color);
+ pen.setStyle(Qt::DashLine);
+ painter.setPen(pen);
+ painter.drawLine(0, height - 1, width, height - 1);
+}
+
+void PreeditLine::paintEvent(QPaintEvent *e) {
+ QWidget::paintEvent(e);
+ QPainter p(this);
+ p.drawPixmap(QPointF(0, 0), *pixmap_, e->rect());
+}
+
+} // namespace FQTerm
+
+#include "fqterm_screen.moc"