📄 textarea.cpp
字号:
/*************************************************************************** * Copyright (C) 2007 by Motorola Commnunity around the World * * lahu3672@googlemail.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 "TextArea.h"TextArea::TextArea(QWidget *parent, const char *name): QFrame(parent, name){ QPixmap::setDefaultOptimization(QPixmap::NormalOptim); mutex.lock(); QFontFactory *factory = qt_fontmanager->factories.at(0); QDiskFont *qdf = new QDiskFont(factory, "Lucida Console", false, 25, 0, "s","/ezxlocal/lucon.ttf"); qt_fontmanager->diskfonts.append(qdf); QFontDatabase::qwsAddDiskFont(qdf); eseq = ESEQ_NONE; esc_seq = ""; defAttrs.pen.setColor(black); font.setFixedPitch(true); font.setFamily("Lucida Console"); font.setPointSize(9); defAttrs.attrs = 0; defAttrs.bgColor = white; defAttrs.textBgColor = white; newAttrs = curAttrs = defAttrs; setFocusPolicy(QWidget::StrongFocus); metrics = new QFontMetrics(font); QRect cr = contentsRect(); crd.setX(cr.left()); crd.setY(cr.top()); stcrd = crd; row = col = 0; topRow = 0; fakeTopRow = 0; ansiChanges = 0; newCrd = crd; newCol = col; newRow = row; canDraw = true; insertMode = false;}TextArea::~TextArea(){ delete metrics; lines.setAutoDelete(true); lines.clear();}void TextArea::pushLine(){ QPixmap *pl = new QPixmap(contentsRect().width(), metrics->height()); lines.append(pl); pl->fill(curAttrs.bgColor);}uint TextArea::drawText(QPixmap *pix, const QPoint &where, const QString &s){ uint len = metrics->width(s); QRect trect = QRect(where, QSize(len, metrics->height())); QPainter p(pix); p.setFont(font); p.setPen(curAttrs.pen); p.fillRect(trect, curAttrs.textBgColor); p.drawText(where.x(), metrics->ascent(), s); p.end(); return metrics->width(s);}void TextArea::showLine(QPixmap *pix, const QPoint &where, uint width, QPaintDevice *dev){ if (!dev) dev = this; bitBlt(dev, where.x(), where.y(), pix, where.x(), 0, width, pix->height());}void TextArea::renderText(int row, const QPoint &where, const QString &s){ if (!s.length()) return; QPixmap *pix = lines.at(row); uint width = drawText(pix, QPoint(where.x(), 0), s); crd.rx() += width;}QPoint TextArea::lineCoord(const QPoint &c){ return QPoint(c.x() - stcrd.y(), 0);}void TextArea::moveChars(uint r, uint from, uint cnt, uint to){ if (to == from) return; QPixmap *line = lines.at(r); QPoint moveFrom = QPoint(getX(r, from), 0); QPoint moveTo = QPoint(getX(r, to), 0); QRect eraseRect; QRect moveRect = QRect(moveFrom, QSize(contentsRect().width() - moveFrom.x(), metrics->height())); bitBlt(line, moveTo, line, moveRect); if (to > from) { eraseRect = QRect(moveFrom, QSize(moveTo.x() - moveFrom.x(), metrics->height())); erase(eraseRect, line); }}void TextArea::erase(const QRect &rect, QPaintDevice *p){ if (!canDraw) return; if (!p) p = this; QPixmap pix(rect.size()); pix.fill(curAttrs.bgColor); bitBlt(p, rect.topLeft(), &pix, pix.rect());}void TextArea::eraseLine(uint row, EraseType type, const QPoint &crd){ QPixmap *line = lines.at(row); switch (type) { case EraseAll: erase(QRect(crd, QSize(contentsRect().width() - crd.x(), metrics->height()))); erase(QRect(lineCoord(crd), QSize(contentsRect().width() - crd.x(), metrics->height())), line); break; case EraseToCurrent: erase(QRect(stcrd.x(), stcrd.y(), crd.x() - stcrd.x(), metrics->height())); erase(QRect(0, 0, lineCoord(crd).x(), metrics->height()), line); break; case EraseToEnd: erase(QRect(crd.x(), crd.y(), contentsRect().width() - crd.x(), metrics->height())); erase(QRect(lineCoord(crd), QSize(contentsRect().width() - crd.x(), metrics->height())), line); break; }}void TextArea::eraseLines(uint from, uint to){ if (to < from) return; for (uint i = from; i <= to; ++i) lines.at(i)->fill(curAttrs.bgColor);}QStringList TextArea::ansiParseArgs(){ return QStringList::split(";", esc_seq);}void TextArea::ansiSetAttrs(){ newAttrs = curAttrs; QStringList args; if (esc_seq.isEmpty()) args.append("0"); else args = ansiParseArgs(); for (QStringList::ConstIterator it = args.begin(); it != args.end(); ++it) { int arg = (*it).toUInt(); bool fg = false; QColor color; if ((arg>=40)&&(arg<=47)) { fg = true; arg-=10; } if ((arg>=30)&&(arg<=37)) { switch (arg-30) { case 0: color = black; break; case 1: color = red; break; case 2: color = green; break; case 3: color = yellow; break; case 4: color = blue; break; case 5: color = magenta; break; case 6: color = cyan; break; case 7: color = white; break; } if (!fg) newAttrs.pen.setColor(color); else { newAttrs.bgColor = color; } }// if (arg == 7) newAttrs.attrs |= ANSI_ATTR_INVERSE; if (arg == 7) { QColor bg = newAttrs.textBgColor; newAttrs.textBgColor = newAttrs.pen.color(); newAttrs.pen.setColor(bg); } if (arg == 2) newAttrs.attrs |= ANSI_ATTR_BOLD; if (arg == 0) newAttrs = defAttrs; ansiChanges |= ANSI_CHANGED_ATTRS; }}void TextArea::ansiGo(uint direction){ uint cnt = 0; int r = 0,c = 0; QStringList args = ansiParseArgs(); if (esc_seq.isEmpty()) cnt = r = c = 1; else if (direction != DIR_ABSOLUTE) { cnt = args[0].toUInt(); } else { r = args[0].toUInt(); c = args[1].toUInt(); } switch (direction) { case DIR_UP: go(row - cnt, col); break; case DIR_DOWN: go(row + cnt, col); break; case DIR_FORWARD: go(row, col + cnt); break; case DIR_BACKWARD: go(row, col - cnt); break; case DIR_ABSOLUTE: go(topRow + r - 1, c - 1); break; } ansiChanges |= ANSI_CHANGED_POSITION;}void TextArea::ansiEraseLine(){ uint type; if (esc_seq.isEmpty()) type = 0; else type = ansiParseArgs()[0].toUInt(); switch (type) { case 0: eraseLine(row, EraseAll, crd); break; case 1: eraseLine(row, EraseToCurrent, crd); break; case 2: eraseLine(row, EraseToEnd, crd); break; }}void TextArea::ansiEraseChars(){ uint cnt; if (esc_seq.isEmpty()) cnt = 1; else cnt = ansiParseArgs()[0].toUInt(); moveChars(row, col + cnt, cnt, col);}void TextArea::ansiInsertChars(){ uint cnt; if (esc_seq.isEmpty()) cnt = 1; else cnt = ansiParseArgs()[0].toUInt(); moveChars(row, col, cnt, col + cnt);} void TextArea::ansiEraseScreen(){ uint type; if (esc_seq.isEmpty()) type = 0; else type = ansiParseArgs()[0].toUInt(); QRect er; switch (type) { case 0: eraseLine(row, EraseToEnd, crd); eraseLines(row + 1, lines.count() - 1); er = QRect(QPoint(stcrd.x(), crd.y() + metrics->height()), contentsRect().bottomRight()); break; case 1: eraseLine(row, EraseToCurrent, crd); eraseLines(topRow, row - 1); er = QRect(stcrd, QPoint(contentsRect().right(), crd.y())); break; case 2: eraseLines(topRow, lines.count() - 1); er = contentsRect(); break; } erase(er);}void TextArea::ansiSetMode(){ if (esc_seq.isEmpty()) return; uint mode = 0; mode = ansiParseArgs()[0].toUInt(); switch (mode) { case 4: insertMode = true; }}void TextArea::ansiResetMode(){ if (esc_seq.isEmpty()) return; uint mode = 0; mode = ansiParseArgs()[0].toUInt(); switch (mode) { case 4: insertMode = false; }} void TextArea::ansiProcess(char ch){ switch (ch) { case 'f': case 'A': ansiGo(DIR_UP); break; case 'B': ansiGo(DIR_DOWN); break; case 'C': ansiGo(DIR_FORWARD); break; case 'D': ansiGo(DIR_BACKWARD); break; case 'H': ansiGo(DIR_ABSOLUTE); break; case 'J': ansiEraseScreen(); break; case 'K': ansiEraseLine(); break; case 'm': ansiSetAttrs(); break; case 'h': ansiSetMode(); break; case 'l': ansiResetMode(); break; case '@': ansiInsertChars(); break; case 'P': ansiEraseChars(); break; default: esc_seq += ch; return; } eseq = ESEQ_ANSI_DONE; esc_seq = "";}int TextArea::insertChar(char ch){ if (eseq == ESEQ_ANSI_STARTED) { ansiProcess(ch); return eseq == ESEQ_ANSI_DONE; } if (eseq == ESEQ_ANSI_DONE) eseq = ESEQ_NONE; if (eseq == ESEQ_STUB) eseq = ESEQ_NONE; if (eseq == ESEQ_STUB2) eseq = ESEQ_STUB; switch (ch) { case ESC_CHAR: if (eseq == ESEQ_NONE) eseq = ESEQ_ESC; else eseq = ESEQ_NONE; break; case '[': if (eseq == ESEQ_ESC) eseq = ESEQ_ANSI_STARTED; else eseq = ESEQ_NONE; break; case '=': case '7': if (eseq == ESEQ_ESC) eseq = ESEQ_STUB; break; case ')': if (eseq == ESEQ_ESC) eseq = ESEQ_STUB2; break; default: eseq = ESEQ_NONE; } return 0;}int TextArea::getX(){ return stcrd.x() + (col)*metrics->maxWidth();}int TextArea::getX(uint r, uint c){ return stcrd.x() + (c)*metrics->maxWidth();}int TextArea::getY(){ return (row - topRow) * metrics->height();}int TextArea::getY(uint r, uint c){ if (r<topRow) return 0; return (r - topRow) * metrics->height();}void TextArea::tab(){ uint nc = col - col%TAB_SIZE + TAB_SIZE; if (nc >= xsize) nc = xsize - 1; crd.rx() = getX(row, nc); col = nc;}void TextArea::back(){ if (!col && !row) return; if (col == 0) { col = xsize-1; row--; crd.rx() = getX(); } else { col--; crd.rx() -= metrics->maxWidth(); }}void TextArea::showTextBlock(uint from, uint to, uint diff){ uint count = to - from + 1; QPoint p = QPoint(0, 0); QSize size(contentsRect().width(), count* metrics->height()); QPoint dstStart(stcrd.x(), diff * metrics->height()); QRect pixBlock(p, size); QPixmap pix(size); for (uint i = from; i <= to; ++i) { showLine(lines.at(i), p, contentsRect().width(), &pix); p.ry() += metrics->height(); } bitBlt(this, dstStart, &pix, pixBlock);}bool TextArea::newlineShift(){ if (row - topRow >= ysize - 1) { if (canDraw) showTextBlock(topRow + 1, topRow + ysize - 1, 0); crd.rx() = stcrd.x(); topRow++; if (canDraw) fakeTopRow++; return true; } else return false;}void TextArea::newline(){ if (!newlineShift()) { crd.ry() += metrics->height(); } row++; if (row >= lines.count()) pushLine(); col = 0; crd.rx() = stcrd.x();}void TextArea::appendChar(const QChar &ch){ QByteArray arr(1); arr[0] = (char)ch; appendText(arr);}void TextArea::appendText(const QByteArray &text, uint offset, bool new_call){ enum {STATE_NONE = 0, STATE_NEWLINE, STATE_RETURN, STATE_TAB, STATE_BELL, STATE_BACKSPACE, STATE_ANSI}; if (new_call) { mutex.lock(); } int state = STATE_NONE; uint i; QString chunk = ""; QPoint dcrd = crd; for (i = offset; i < text.size(); ++i) { if (state != STATE_NONE) break; switch (text[i]) { case '\n': state = STATE_NEWLINE; break; case '\r': state = STATE_RETURN; break; case '\t': state = STATE_TAB; break; case '\x7': state = STATE_BELL; break; case '\x8': state = STATE_BACKSPACE; break; default: int ret = insertChar(text[i]); if (eseq == ESEQ_NONE) { if (col == xsize) { state = STATE_NEWLINE; i--; } else { if (insertMode) { moveChars(row, col, xsize - col, col + 1); } chunk += text[i]; state = STATE_NONE; col++; } } else { if (ret) state = STATE_ANSI; } } } if (chunk.length()) { renderText(row, dcrd, chunk); } if (state != STATE_NONE) { switch (state) { case STATE_NEWLINE: insertMode = false; if (canDraw) showLine(lines.at(row), QPoint(stcrd.x(), crd.y()), contentsRect().width()); newline(); break; case STATE_RETURN: col = 0; crd.rx() = stcrd.x(); break; case STATE_BACKSPACE: back(); break; case STATE_TAB: tab(); break; case STATE_ANSI: if (ansiChanges & ANSI_CHANGED_ATTRS) curAttrs = newAttrs; if (ansiChanges & ANSI_CHANGED_POSITION) { if (canDraw) showLine(lines.at(row), QPoint(stcrd.x(), crd.y()), contentsRect().width()); row = newRow; col = newCol; crd = newCrd; } ansiChanges = 0; break; } appendText(text, i, false); } else { if (canDraw) showLine(lines.at(row), QPoint(stcrd.x(), crd.y()), contentsRect().width()); mutex.unlock(); }}void TextArea::scroll(int delta){ if (lines.count() <= ysize) return; uint udelta; if (delta > 0) { udelta = delta; delta = (fakeTopRow >= udelta) ? udelta : fakeTopRow; } else { udelta = -delta; uint lastline = fakeTopRow + ysize - 1; if (lastline >= lines.count()) { return; } delta = (lines.count() - lastline >= udelta) ? -udelta : (lastline - lines.count() + 1); } if (!delta) { return; } showTextBlock(fakeTopRow - delta, fakeTopRow + ysize - delta - 1, 0); fakeTopRow-=delta; canDraw = fakeTopRow == topRow; return;}void TextArea::home(){ mutex.lock(); fakeTopRow = topRow; showTextBlock(topRow, lines.count() - 1, 0); canDraw = true; mutex.unlock();}void TextArea::drawContents(QPainter *painter){ mutex.lock(); uint to = lines.count() - fakeTopRow; if (to > ysize - 1) to = fakeTopRow + ysize - 1; else to += fakeTopRow - 1; showTextBlock(fakeTopRow, to, 0); mutex.unlock();}void TextArea::resizeEvent(QResizeEvent *event){ QRect cr = contentsRect(); xsize = cr.width()/metrics->maxWidth(); ysize = cr.height()/metrics->height(); pushLine(); emit configured(); mutex.unlock(); }void TextArea::getSize(struct winsize *ws){ ws->ws_col = xsize; ws->ws_row = ysize; ws->ws_xpixel = contentsRect().width(); ws->ws_ypixel = contentsRect().height();}void TextArea::go(uint r, uint c){ if (r - topRow >= ysize) r = topRow + ysize - 1; if (c >= xsize) c = xsize - 1; int delta = r - lines.count() + 1; while (delta-->0) pushLine(); newRow = r; newCol = c; newCrd.rx() = getX(r, c); newCrd.ry() = getY(r, c);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -