diff options
author | iroul <iroul@iroul-VirtualBox.(none)> | 2014-04-04 07:35:14 -0700 |
---|---|---|
committer | iroul <iroul@iroul-VirtualBox.(none)> | 2014-04-04 07:35:14 -0700 |
commit | afd34f2893a06a3aecf17e8e83b1df6ed2ae91a2 (patch) | |
tree | 851102abc55d91a1b76e63e9e89f9a7733da95b5 /src/terminal/fqterm_buffer.cpp | |
parent | c4b028ad53f7b362a864de24828d7cc39ff67b0a (diff) | |
download | fqterm-afd34f2893a06a3aecf17e8e83b1df6ed2ae91a2.tar.xz |
move to my github.
Diffstat (limited to 'src/terminal/fqterm_buffer.cpp')
-rw-r--r-- | src/terminal/fqterm_buffer.cpp | 998 |
1 files changed, 998 insertions, 0 deletions
diff --git a/src/terminal/fqterm_buffer.cpp b/src/terminal/fqterm_buffer.cpp new file mode 100644 index 0000000..c00e079 --- /dev/null +++ b/src/terminal/fqterm_buffer.cpp @@ -0,0 +1,998 @@ +/*************************************************************************** + * 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 <algorithm> +#include <map> +#include <vector> + +#include <QString> +#include <QRect> +#include <QRegExp> + +#include "fqterm.h" +#include "common.h" +#include "fqterm_buffer.h" +#include "fqterm_text_line.h" + +namespace FQTerm { + +typedef std::vector<int> TabStops; + +/* staight from linux/drivers/char/consolemap.c, GNU GPL:ed */ +static const UTF16 VT_SPECIAL_GRAPHICS_TABLE[256]={ + /* VT100 graphics mapped to Unicode */ + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x00a0, + 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, + 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, + 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, + 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x007f, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, + 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, + 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, + 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, + 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, + 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, + 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, + 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, + 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, + 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, + 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, + 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, + 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff +}; + +FQTermBuffer::FQTermBuffer(int column, int row, int max_hist_line, bool is_bbs) { + tab_stops_ = new TabStops; + num_rows_ = row; + num_columns_ = column; + max_num_hist_lines_ = max_hist_line; + is_bbs_ = is_bbs; + + num_hist_lines_ = 0; + + while (text_lines_.count() < num_rows_) { + text_lines_.append(new FQTermTextLine(num_columns_)); + } + + top_row_ = 0; + bottom_row_ = num_rows_ - 1; + + is_g0_used_ = true; + + is_insert_mode_ = false; + is_ansi_mode_ = true; + is_newline_mode_ = false; + is_cursor_mode_ = false; + is_numeric_mode_ = true; + is_origin_mode_ = false; + is_autowrap_mode_ = false; + is_autorepeat_mode_ = true; + is_lightbg_mode_ = false; + + selection_start_ = QPoint(-1, -1); + selection_end_ = QPoint(-1, -1); +} + +FQTermBuffer::~FQTermBuffer(){ + foreach(FQTermTextLine *line, text_lines_) { + delete line; + } + + delete (TabStops *)tab_stops_; +} + +FQTermBuffer::Caret::Caret() + : column_(0), + row_(0), + color_(NO_COLOR), + attr_(NO_ATTR), + G0_(ASCII_SET), + G1_(ASCII_SET) { +} + +void FQTermBuffer::setTermSize(int col, int row) { + FQ_TRACE("term", 3) << "Change term size to " + << col << "x" << row; + + if (num_columns_ == col && num_rows_ == row) { + return; + } + + clearSelect(); + + if (num_rows_ < row) { + for (int i = 0; i < row - num_rows_; i++) { + text_lines_.append(new FQTermTextLine(col)); + } + } else if (num_rows_ > row) { + for (int i = 0; i < num_rows_ - row; i++) { + delete text_lines_.takeLast(); + } + } + + num_columns_ = col; + num_rows_ = row; + + top_row_ = 0; + bottom_row_ = num_rows_ - 1; + + // TODO: how about saved caret column? + // TODO: why make last saved equal to current position? + last_saved_caret_.row_ = caret_.row_ = qMin(caret_.row_, row - 1); + + for (int i = 0; i < text_lines_.size(); ++i) { + text_lines_.at(i)->setMaxCellCount(num_columns_); + } + +// this->clearArea(0, 0, num_columns_, num_rows_, 0, 0); + // this->moveCaretTo(0, 0); + + moveCaretTo(qMin(caret_.column_, col - 1), qMin(caret_.row_, row - 1)); + + emit onSetTermSize(col, row); + emit termSizeChanged(num_columns_, num_rows_); +} + +int FQTermBuffer::getNumColumns() const { + return num_columns_; +} + +int FQTermBuffer::getNumRows() const { + return num_rows_; +} + +int FQTermBuffer::getNumLines() const { + return num_rows_ + num_hist_lines_; +} + +const FQTermTextLine *FQTermBuffer::getTextLineInBuffer(int line_index) const { + return text_lines_.value(line_index, NULL); +} + +const FQTermTextLine *FQTermBuffer::getTextLineInTerm(int line_index) const { + return text_lines_.value(line_index + num_hist_lines_, NULL); +} + +void FQTermBuffer::setCurrentAttr(unsigned char color, unsigned char attr) { + caret_.color_ = color; + caret_.attr_ = attr; +} + +void FQTermBuffer::writeText(const QString &str, int charstate) { + + QString cstr = str; + + FQ_TRACE("term", 8) << "Add text: \"" << cstr << "\""; + +#if 0 + if ((is_g0_used_ && caret_.G0_ == SPECIAL_GRAPHICS) || + (!is_g0_used_ && caret_.G1_ == SPECIAL_GRAPHICS)) { + + // FIXME: will this break the utf-16 sequence? + for (int i = 0; i < cstr.size(); ++i) { + if (cstr[i] >= 0137 && cstr[i] <= 0176) { + cstr[i] = VT_SPECIAL_GRAPHICS_TABLE[cstr.at(i).unicode()]; + } + } + } +#endif + + // Insert the str into the buffer. Different pieces of text might be + // written into different lines. + while (!cstr.isEmpty()) { + FQTermTextLine *line = text_lines_.value(num_hist_lines_ + caret_.row_, NULL); + + if (line == NULL) { + FQ_TRACE("error", 0) << "Error: setBuffer null line"; + return ; + } + + if (charstate & FQTermTextLine::SECONDPART) { + moveCaretOffset(-1, 0); + } + + if (caret_.column_ >= (int)line->getMaxCellCount()) { + // move the caret to the next line. + moveCaretTo(0, caret_.row_ + 1, true); + continue; + } + + if (caret_.column_ > (int)line->getWidth()) { + line->appendWhiteSpace(caret_.column_ - line->getWidth()); + } + + unsigned cell_begin = line->getCellBegin(caret_.column_); + int max_width = line->getMaxCellCount() - cell_begin; + int element_consumed = 0; + int width = get_str_width((const UTF16 *)cstr.data(), cstr.size(), + max_width, element_consumed); + + if (width < 0) { + break; + } + + if (is_insert_mode_) { + // FIXEME: How to move cursor if the entire line is wider than + // line->getMaxCellCount() after insertion? + line->insertText((UTF16 *)cstr.data(), element_consumed, cell_begin, + caret_.color_, caret_.attr_, charstate); + if ((int)line->getWidth() > num_columns_) { + line->deleteText(num_columns_, line->getWidth()); + } + } else { + line->replaceText((UTF16 *)cstr.data(), element_consumed, + cell_begin, qMin(cell_begin + width, line->getWidth()), + caret_.color_, caret_.attr_, charstate); + } + moveCaretOffset(width, 0); + + if (element_consumed == 0) + { + element_consumed = 1; + } + + if (element_consumed == cstr.size()) { + break; + } + + cstr.remove(0, element_consumed); + } +} + +void FQTermBuffer::lineFeed() { + FQ_TRACE("term", 8) << "add a new line"; + if (caret_.row_ == bottom_row_) + scrollLinesInTerm(top_row_, 1); + if (is_newline_mode_) { + moveCaretOffset(-caret_.column_, 1); + } else { + moveCaretOffset(0, 1); + } +} + +void FQTermBuffer::tab() { + FQ_TRACE("term", 8) << "add a tab"; + int x = getTabStop(caret_.column_); + + moveCaretTo(x, caret_.row_); +} + +int FQTermBuffer::getTabStop(int column) { + + std::vector<int> &tabs = *(TabStops *)tab_stops_; + + if (tabs.size() == 0) { + int res = ((caret_.column_) / 8 + 1) * 8; + return (res < num_columns_) ? res : num_columns_ - 1; + } + + std::vector<int>::iterator it = + std::upper_bound(tabs.begin(), tabs.end(), column); + + int res = num_columns_ - 1; + + if (it != tabs.end() && *it < res) { + res = *it; + } + + if (res > num_columns_ - 1) { + res = num_columns_ - 1; + } + + return res; +} + +void FQTermBuffer::addTabStop() { + // FIXME: what if the caret is located beyond the term temporarily. + int x = caret_.column_; + + std::vector<int> &tabs = *(TabStops *)tab_stops_; + + std::vector<int>::iterator it = + std::lower_bound(tabs.begin(), tabs.end(), x); + + if (it == tabs.end() || *it != x) { + tabs.insert(it, x); + } +} + +void FQTermBuffer::clearTabStop(bool clear_all) { + // FIXME: what if the caret is located beyond the term temporarily. + std::vector<int> &tabs = *(TabStops *)tab_stops_; + + if (clear_all) { + tabs.clear(); + return; + } + + int x = caret_.column_; + + std::vector<int>::iterator it = + std::lower_bound(tabs.begin(), tabs.end(), x); + + if (it != tabs.end() && *it == x) { + tabs.erase(it); + } +} + +void FQTermBuffer::setMargins(int top, int bottom) { + FQ_TRACE("term", 3) << "Set margins: [" << top << ", " << bottom << "]"; + + top_row_ = qMax(top, 0); + bottom_row_ = qMin(qMax(bottom, 0), num_rows_ - 1); + + if (is_origin_mode_) { + moveCaretTo(0, top_row_); + } else { + moveCaretTo(0, 0); + } +} + +void FQTermBuffer::moveCaretTo(int column, int row, bool scroll_if_necessary) { + if (row != caret_.row_) + emit caretChangeRow(); + + FQ_TRACE("term", 5) << "Move caret to (" << column << ", " << row << ")"; + + //If th + + // detect index boundary + if (column >= num_columns_) { + column = num_columns_; + } + if (column < 0) { + column = 0; + } + + int scroll_lines = 0; + + int stop = is_origin_mode_ ? top_row_ : 0; + if (row < stop) { + scroll_lines = row - stop; + row = stop; + } + + stop = is_origin_mode_ ? bottom_row_ : num_rows_ - 1; + if (row > stop) { + scroll_lines = row - stop; + row = stop; + } + + // Set dirty flag for cells of the last caret position. + FQTermTextLine *line = text_lines_.value(num_hist_lines_ + caret_.row_, NULL); + if (0 <= caret_.column_ && caret_.column_ < (int)line->getWidth()) { + unsigned cell_begin = line->getCellBegin(caret_.column_); + unsigned cell_end = line->getCellBegin(caret_.column_ + 1); + line->setDirtyFlag(cell_begin, cell_end); + } + + if (scroll_if_necessary) { + scrollLinesInTerm(top_row_, scroll_lines); + } + + caret_.column_ = column; + caret_.row_ = row; +} + +void FQTermBuffer::moveCaretOffset(int column_offset, int row_offset, bool scroll_if_necessary) { + if (caret_.column_ >= num_columns_) { + // it's only allowed that the caret_.column_ >= num_columns_ + // temporarily when a sequence of normal text is received. if we + // found caret_.column_ is out of bounds in other case, we should + // correct it first. + if (is_bbs_) { + // but the BBS (newsmth.net) assumes that the caret could be + // located out of the screen :( + } else { + caret_.column_ = num_columns_ - 1; + } + } + + if (caret_.column_ + column_offset < 0) { + column_offset = -caret_.column_; + } + + moveCaretTo(caret_.column_ + column_offset, caret_.row_ + row_offset, + scroll_if_necessary); +} + +void FQTermBuffer::changeCaretPosition(int coloumn, int row) { + if (is_origin_mode_) { + moveCaretTo(coloumn, row + top_row_); + } else { + moveCaretTo(coloumn, row); + } +} + +void FQTermBuffer::saveCaret() { + FQ_TRACE("term", 5) << "save the caret."; + last_saved_caret_ = caret_; +} + +void FQTermBuffer::restoreCaret() { + FQ_TRACE("term", 5) << "restore the caret."; + moveCaretTo(last_saved_caret_.column_, last_saved_caret_.row_); + caret_ = last_saved_caret_; +} + +void FQTermBuffer::SelectVtCharacterSet(VtCharSet charset, bool G0) { + if (G0) { + caret_.G0_ = charset; + } else { + caret_.G1_ = charset; + } +} + +void FQTermBuffer::invokeCharset(bool G0) { + if (G0) { + is_g0_used_ = true; + } else { + is_g0_used_ = false; + } +} + +void FQTermBuffer::carriageReturn() { + FQ_TRACE("term", 8) << "carrige return"; + moveCaretOffset(-caret_.column_, 0); +} + +int FQTermBuffer::getCaretColumn() const { + return caret_.column_; +} + +int FQTermBuffer::getCaretRow() const { + return caret_.row_; +} + +int FQTermBuffer::getCaretLine() const { + return caret_.row_ + num_hist_lines_; +} + +// erase functions +void FQTermBuffer::eraseText(int cell_count) { + FQ_TRACE("term", 8) << "erase " << cell_count << " cell(s) of text"; + + const FQTermTextLine *line = text_lines_.at(caret_.row_ + num_hist_lines_); + + int x = line->getWidth() - caret_.column_; + + clearArea(caret_.column_, caret_.row_, + qMin(cell_count, x), 1, + caret_.color_, caret_.attr_); +} + +void FQTermBuffer::deleteText(int cell_count) { + FQ_TRACE("term", 8) << "delete " << cell_count << " cell(s) of text"; + + FQTermTextLine *line = text_lines_.at(caret_.row_ + num_hist_lines_); + + int x = line->getWidth() - caret_.column_; + + if (cell_count >= x) { + line->deleteText(caret_.column_, line->getWidth()); + } else { + line->deleteText(caret_.column_, caret_.column_ + cell_count); + } +} + +void FQTermBuffer::fillScreenWith(char c) { + FQ_TRACE("term", 5) << "fill screen with '" << c << "'"; + for (int i = 0; i < num_rows_; i++) { + FQTermTextLine *line = text_lines_[i + num_hist_lines_]; + line->deleteAllText(); + line->appendWhiteSpace(num_columns_, NO_COLOR, NO_ATTR, c); + } +} + +void FQTermBuffer::insertSpaces(int count) { + FQ_TRACE("term", 8) << "insert " << count << " white space(s)"; + + FQTermTextLine *line = text_lines_.at(caret_.row_ + num_hist_lines_); + + int x = line->getWidth() - caret_.column_; + + if (count >= x) { + clearArea(caret_.column_, caret_.row_, x, caret_.row_, + caret_.color_, caret_.attr_); + } else { + line->insertWhiteSpace(count, caret_.column_, caret_.color_, caret_.attr_); + } +} + +void FQTermBuffer::deleteLines(int line_count) { + FQ_TRACE("term", 8) << "delete " << line_count << " line(s)"; + + int y = bottom_row_ - caret_.row_; + + if (line_count >= y) { + clearArea(0, caret_.row_, -1, y, caret_.color_, caret_.attr_); + } else { + scrollLinesInTerm(caret_.row_, line_count); + } +} + +void FQTermBuffer::insertLines(int count) { + FQ_TRACE("term", 8) << "insert " << count << " line(s)"; + + int y = bottom_row_ - caret_.row_; + + if (count >= y) { + clearArea(0, caret_.row_, -1, y, caret_.color_, caret_.attr_); + } else { + scrollLinesInTerm(caret_.row_, -count); + } +} + +void FQTermBuffer::eraseToLineEnd() { + FQ_TRACE("term", 8) << "erase to line end"; + + clearArea(caret_.column_, caret_.row_, -1, 1, caret_.color_, caret_.attr_); +} + +void FQTermBuffer::eraseToLineBegin() { + FQ_TRACE("term", 8) << "erase to line begin"; + clearArea(0, caret_.row_, caret_.column_ + 1, 1, caret_.color_, caret_.attr_); +} + +void FQTermBuffer::eraseEntireLine() { + FQ_TRACE("term", 8) << "erase entire line"; + clearArea(0, caret_.row_, -1, 1, caret_.color_, caret_.attr_); +} + +// Set a line of text on screen to have been changed from start to end. +void FQTermBuffer::setLineChanged(int index, int cell_begin, int cell_end) { + FQ_ASSERT(0 <= index && index < num_rows_ + num_hist_lines_); + text_lines_[index]->setDirtyFlag(cell_begin, cell_end); +} + +void FQTermBuffer::clearLineChanged(int index) { + FQ_ASSERT(0 <= index && index < num_rows_ + num_hist_lines_); + text_lines_[index]->clearDirtyFlag(); +} + +void FQTermBuffer::setLineAllChanged(int index) { + FQ_ASSERT(0 <= index && index < num_rows_ + num_hist_lines_); + text_lines_[index]->setAllDirty(); +} + +void FQTermBuffer::scrollLinesInTerm(int startRow, int numRows) { + if (numRows == 0 || startRow > bottom_row_) { + return ; + } + if (startRow < top_row_) { + startRow = top_row_; + } + + // TODO: performance issue here. Reuse the old text lines. + if (numRows > 0) { + //We are scrolling the whole screen. + if (startRow == 0 && caret_.row_ == num_rows_ - 1) { + addHistoryLine(numRows); + } else { + // delete lines from startRow, insert lines on the bottom_row_. + while (numRows) { + delete text_lines_.takeAt(num_hist_lines_ + startRow); + text_lines_.insert(num_hist_lines_ + bottom_row_, new FQTermTextLine(num_columns_)); + numRows--; + } + } + } + + // TODO: performance issue here. Reuse the old text lines. + if (numRows < 0) { + // delete lines from bottom_row_, insert lines in the startRow. + while (numRows) { + delete text_lines_.takeAt(num_hist_lines_ + bottom_row_); + text_lines_.insert(num_hist_lines_ + startRow, new FQTermTextLine(num_columns_)); + numRows++; + } + } + + for (int i = num_hist_lines_ + startRow; i <= num_hist_lines_ + bottom_row_; i++) { + text_lines_.at(i)->setAllDirty(); + } +} + +void FQTermBuffer::eraseToTermEnd() { + FQ_TRACE("term", 8) << "erase to term end"; + + if (caret_.column_ == 0 && caret_.row_ == 0) { + eraseEntireTerm(); + return ; + } + + clearArea(caret_.column_, caret_.row_, -1, 1, caret_.color_, caret_.attr_); + + if (caret_.row_ < bottom_row_) { + clearArea(0, caret_.row_ + 1, + -1, bottom_row_ - caret_.row_, + caret_.color_, caret_.attr_); + } +} + +void FQTermBuffer::eraseToTermBegin() { + FQ_TRACE("term", 8) << "erase to term begin"; + if (caret_.column_ == num_columns_ - 1 && caret_.row_ == num_rows_ - 1) { + eraseEntireTerm(); + return ; + } + + clearArea(0, caret_.row_, caret_.column_ + 1, 1, caret_.color_, caret_.attr_); + if (caret_.row_ > top_row_) { + clearArea(0, top_row_, -1, caret_.row_, caret_.color_, caret_.attr_); + } +} + +void FQTermBuffer::eraseEntireTerm() { + FQ_TRACE("term", 8) << "erase entire term"; + addHistoryLine(num_rows_); + clearArea(0, 0, num_columns_, num_rows_, caret_.color_, caret_.attr_); +} + +// width = -1 : clear to end +void FQTermBuffer::clearArea(int startColumn, int startRow, + int width, int height, + unsigned char color, unsigned char attr) { + QByteArray cstr; + + FQTermTextLine *line; + + if (startRow + height > num_rows_) { + height = num_rows_ - startRow; + } + + for (int i = startRow; i < height + startRow; i++) { + line = text_lines_[i + num_hist_lines_]; + + int w = width; + + if (startColumn < num_columns_) { + if (w == -1) { + w = num_columns_ - startColumn; + } + + int endX = startColumn + w; + if (endX > (int)line->getWidth()) { + endX = line->getWidth(); + } + + if(startColumn>=endX) + { + continue; + } + + line->replaceWithWhiteSpace(w, startColumn, endX, color, attr); + } + } +} + +void FQTermBuffer::addHistoryLine(int n) { + bool is_full = (num_hist_lines_ == max_num_hist_lines_); + + // TODO: performance issue. Reuse the old lines. + while (n) { + if (num_hist_lines_ == max_num_hist_lines_) { + delete text_lines_.takeFirst(); + } + + text_lines_.append(new FQTermTextLine(num_columns_)); + num_hist_lines_ = qMin(num_hist_lines_ + 1, max_num_hist_lines_); + n--; + } + + for (int i = num_hist_lines_ + 0; i < num_hist_lines_ + bottom_row_; i++) { + text_lines_.at(i)->setAllDirty(); + } + + if (!is_full) { + emit bufferSizeChanged(); + } +} + +void FQTermBuffer::startDecode() { + last_saved_caret_.column_ = caret_.column_; + last_saved_caret_.row_ = caret_.row_; +} + +void FQTermBuffer::endDecode() { + if (last_saved_caret_.row_ < num_rows_) { + FQTermTextLine *line = text_lines_[last_saved_caret_.row_ + num_hist_lines_]; + + line->safelySetDirtyFlag(last_saved_caret_.column_, + last_saved_caret_.column_ + 1); + } + + if (caret_.row_ < num_rows_) { + FQTermTextLine *line = text_lines_.at(caret_.row_ + num_hist_lines_); + + line->safelySetDirtyFlag(caret_.column_, caret_.column_ + 1); + } + + clearSelect(); +} + +void FQTermBuffer::setMode(TermMode mode) { + FQ_TRACE("term", 8) << "set mode " << mode; + + switch (mode) { + case INSERT_MODE: + is_insert_mode_ = true; + break; + case CURSOR_MODE: + is_cursor_mode_ = true; + break; + case ANSI_MODE: + is_ansi_mode_ = true; + case NUMERIC_MODE: + is_numeric_mode_ = true; + break; + case SMOOTH_MODE: + is_smoothscroll_mode_ = true; + break; + case NEWLINE_MODE: + is_newline_mode_ = true; + break; + case ORIGIN_MODE: + is_origin_mode_ = true; + this->moveCaretTo(0, top_row_); + break; + case AUTOWRAP_MODE: + is_autowrap_mode_ = true; + break; + case AUTOREPEAT_MODE: + is_autorepeat_mode_ = true; + break; + case LIGHTBG_MODE: + is_lightbg_mode_ = true; + break; + default: + break; + } +} + +void FQTermBuffer::resetMode(TermMode mode) { + FQ_TRACE("term", 8) << "reset mode " << mode; + + switch (mode) { + case INSERT_MODE: + is_insert_mode_ = false; + break; + case CURSOR_MODE: + is_cursor_mode_ = false; + break; + case ANSI_MODE: + is_ansi_mode_ = false; + case NUMERIC_MODE: + is_numeric_mode_ = false; + break; + case SMOOTH_MODE: + is_smoothscroll_mode_ = false; + break; + case NEWLINE_MODE: + is_newline_mode_ = false; + break; + case ORIGIN_MODE: + is_origin_mode_ = false; + this->moveCaretTo(0, 0); + break; + case AUTOWRAP_MODE: + is_autowrap_mode_ = false; + break; + case AUTOREPEAT_MODE: + is_autorepeat_mode_ = false; + break; + case LIGHTBG_MODE: + is_lightbg_mode_ = false; + break; + default: + break; + } +} + +void FQTermBuffer::setSelect(const QPoint &pt1, const QPoint &pt2) { + QPoint ptSelStart, ptSelEnd; + + if (pt1.y() == pt2.y()) { + ptSelStart = pt1.x() < pt2.x() ? pt1 : pt2; + ptSelEnd = pt1.x() > pt2.x() ? pt1 : pt2; + } else { + ptSelStart = pt1.y() < pt2.y() ? pt1 : pt2; + ptSelEnd = pt1.y() > pt2.y() ? pt1 : pt2; + } + + int y1 = ptSelStart.y(); + int y2 = ptSelEnd.y(); + + if (!(selection_start_ == QPoint(-1, -1) + && selection_end_ == QPoint(-1, -1))) { + y1 = qMin(y1, selection_start_.y()); + y2 = qMax(y2, selection_end_.y()); + } + + for (int i = y1; i <= y2; i++) { + text_lines_.value(i, NULL)->setAllDirty(); + } + + selection_start_ = ptSelStart; + selection_end_ = ptSelEnd; +} + +void FQTermBuffer::clearSelect() { + if (selection_start_ == QPoint(-1, -1) && selection_end_ == QPoint(-1, -1)) { + return; + } + + for (int i = selection_start_.y(); i <= selection_end_.y(); i++) { + text_lines_.value(i, NULL)->setAllDirty(); + } + + selection_start_ = selection_end_ = QPoint(-1, -1); +} + +bool FQTermBuffer::isSelected(int line_index) const { + if (selection_start_ == QPoint(-1, -1) && selection_end_ == QPoint(-1, -1)) { + return false; + } else { + return line_index >= selection_start_.y() && line_index <= selection_end_.y(); + } +} + +bool FQTermBuffer::isSelected(const QPoint &cell, bool is_rect_sel) const { + if (selection_start_ == QPoint(-1, -1) && selection_end_ == QPoint(-1, -1)) { + return false; + } + + if (cell.y() < 0 || cell.y() >= getNumLines()) { + return false; + } + + int x1 = selection_start_.x(); + int y1 = selection_start_.y(); + + int x2 = selection_end_.x(); + int y2 = selection_end_.y(); + + if (cell.y() < y1 || cell.y() > y2) + return false; + + const FQTermTextLine *line = this->getTextLineInBuffer(cell.y()); + + int cell_begin = 0; + int cell_end = cell.x() + 1; + + if (is_rect_sel) { + int minx = qMin(x1, x2); + int maxx = qMax(x1, x2); + cell_begin = line->getCellBegin(qMin(minx, (int)line->getWidth())); + cell_end = line->getCellEnd(qMin(maxx + 1, (int)line->getWidth())); + } else { + if (cell.y() == y1) { + cell_begin = line->getCellBegin(qMin(x1, (int)line->getWidth())); + } + if (cell.y() == y2) { + cell_end = line->getCellEnd(qMin(x2 + 1, (int)line->getWidth())); + } + } + + return cell_begin <= cell.x() && cell.x() < cell_end; +} + +static void removeTrailSpace(QString &line) { + for (int last_non_space = line.size() - 1; + last_non_space >= 0; --last_non_space) { + QChar a = line.at(last_non_space); + if (!a.isSpace()) { + line.resize(last_non_space + 1); + break; + } + + if (last_non_space == 0) { + line.resize(0); + } + } +} + + +QString FQTermBuffer::getTextSelected(bool is_rect_sel, bool is_color_copy, + const QByteArray &escape) const { + QString cstrSelect; + QString strTemp; + + if (selection_start_ == QPoint(-1, -1) && selection_end_ == QPoint(-1, -1)) { + return cstrSelect; + } + + QRect rc; + + for (int i = selection_start_.y(); i <= selection_end_.y(); i++) { + strTemp.clear(); + rc = getSelectRect(i, is_rect_sel); + + FQTermTextLine *line = text_lines_.at(i); + unsigned cell_begin = qMax(rc.left(), 0); + unsigned cell_end = qMin(rc.right() + 1, (int)line->getWidth()); + FQ_ASSERT(rc.left() + rc.width() == rc.right() + 1); + + if (cell_begin < cell_end) { + cell_begin = line->getCellBegin(cell_begin); + cell_end = line->getCellEnd(cell_end); + + if (is_color_copy) { + line->getAnsiText(cell_begin, cell_end, strTemp, escape); + } else { + line->getPlainText(cell_begin, cell_end, strTemp); + } + } + + removeTrailSpace(strTemp); + + cstrSelect += strTemp; + + // add newline except the last line + if (i != selection_end_.y()) { + cstrSelect += OS_NEW_LINE; + } + } + + return cstrSelect; +} + +QRect FQTermBuffer::getSelectRect(int line_index, bool is_rect_sel) const { + FQ_ASSERT(isSelected(line_index)); + + if (is_rect_sel) { + return QRect(qMin(selection_start_.x(), selection_end_.x()), line_index, + abs(selection_end_.x() - selection_start_.x()) + 1, 1); + } else if (selection_start_.y() == selection_end_.y()) { + return QRect(selection_start_.x(), line_index, + qMin(selection_end_.x(), num_columns_) - selection_start_.x() + 1, 1); + } else if (line_index == selection_start_.y()) { + return QRect(selection_start_.x(), line_index, + qMax(0, num_columns_ - selection_start_.x()), 1); + } else if (line_index == selection_end_.y()) { + return QRect(0, line_index, qMin(num_columns_, selection_end_.x() + 1), 1); + } else { + return QRect(0, line_index, num_columns_, 1); + } +} + +void FQTermBuffer::scrollTerm(int numRows) { + scrollLinesInTerm(caret_.row_, numRows); + moveCaretOffset(0, numRows); +} + + +} // namespace FQTerm + +#include "fqterm_buffer.moc" |