summaryrefslogtreecommitdiff
path: root/src/terminal/fqterm_text_line.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/terminal/fqterm_text_line.cpp')
-rw-r--r--src/terminal/fqterm_text_line.cpp541
1 files changed, 541 insertions, 0 deletions
diff --git a/src/terminal/fqterm_text_line.cpp b/src/terminal/fqterm_text_line.cpp
new file mode 100644
index 0000000..ed6d164
--- /dev/null
+++ b/src/terminal/fqterm_text_line.cpp
@@ -0,0 +1,541 @@
+/***************************************************************************
+ * 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 <QRegExp>
+#include <QString>
+
+#include "fqterm.h"
+#include "fqterm_text_line.h"
+
+namespace FQTerm {
+
+#ifndef NDEBUG
+//#define TEXT_LINE_CHECK
+#else
+//#define TEXT_LINE_CHECK
+#endif
+
+#ifndef TEXT_LINE_CHECK
+void FQTermTextLine::verifyTextLine() const {}
+void FQTermTextLine::verifyCellRange(unsigned, unsigned) const {}
+#else
+void FQTermTextLine::verifyTextLine() const {
+ unsigned last_cell = 0;
+ unsigned cur_cell = last_cell;
+
+ if (cells_.size() == 0
+ || cell_colors_.size() == 0
+ || cell_attrs_.size() == 0
+ || chars_.size() == 0
+ ) {
+ FQ_VERIFY(cells_.size() == 0);
+ FQ_VERIFY(cell_colors_.size() == 0);
+ FQ_VERIFY(cell_attrs_.size() == 0);
+ FQ_VERIFY(chars_.size() == 0);
+ return;
+ }
+
+ FQ_VERIFY(max_cell_count_ < 65536);
+ FQ_VERIFY(chars_.size() < 65536);
+ FQ_VERIFY(cells_.size() <= max_cell_count_);
+ FQ_VERIFY(cells_.size() == cell_colors_.size());
+ FQ_VERIFY(cells_.size() == cell_attrs_.size());
+
+ const UTF16 *start = &chars_[0];
+ const UTF16 *end = start + chars_.size();
+
+ for (const UTF16 *p = start; p < end;) {
+ int w = mk_advace_at_least_one_cell(&p, end);
+
+ FQ_VERIFY(w >= 0); // should be valid unicode string.
+
+ cur_cell += w;
+
+ for (unsigned i = last_cell; i < cur_cell; ++i) {
+ unsigned char_end = p - start;
+ FQ_VERIFY(cells_[i] == char_end);
+ }
+
+ last_cell = cur_cell;
+ }
+
+ FQ_VERIFY(cur_cell == getWidth());
+ FQ_VERIFY(cells_.back() == chars_.size());
+}
+
+void FQTermTextLine::verifyCellRange(unsigned cell_begin, unsigned cell_end) const {
+ FQ_VERIFY(cell_begin <= cell_end);
+ FQ_VERIFY(cell_end <= getWidth());
+
+ unsigned real_cell_begin = getCellBegin(cell_begin);
+ FQ_VERIFY(real_cell_begin == cell_begin);
+
+ unsigned real_cell_end = getCellBegin(cell_end);
+ FQ_VERIFY(real_cell_end == cell_end);
+
+ unsigned char_begin = getCharBegin(cell_begin);
+ FQ_VERIFY(char_begin <= chars_.size());
+
+ unsigned char_end = getCharEnd(cell_end);
+ FQ_VERIFY(char_begin <= char_end);
+ FQ_VERIFY(char_end <= chars_.size());
+
+}
+
+#endif
+
+FQTermTextLine::FQTermTextLine(unsigned max_cell_count)
+ : max_cell_count_(max_cell_count),
+ dirty_cell_begin_(0),
+ dirty_cell_end_(max_cell_count),
+ stored_color_(0),
+ stored_attr_(0){
+ cells_.reserve(max_cell_count_);
+ cell_colors_.reserve(max_cell_count_);
+ cell_attrs_.reserve(max_cell_count_);
+ chars_.reserve(max_cell_count_);
+}
+
+FQTermTextLine::~FQTermTextLine() {
+}
+
+void FQTermTextLine::setMaxCellCount(unsigned max_cell_count) {
+ // FIXME: how about dirty flag?
+ max_cell_count_ = max_cell_count;
+
+ if (cells_.size() > max_cell_count_) {
+ // TODO_UTF16: what if an incomplete utf-16 byte left.
+ cells_.resize(max_cell_count_);
+ cell_colors_.resize(max_cell_count_);
+ cell_attrs_.resize(max_cell_count_);
+ chars_.resize(cells_.back());
+ }
+}
+
+// Whether this line of text contains blink characters.
+bool FQTermTextLine::hasBlink() const {
+ bool blink = false;
+
+ char tempea;
+ for (unsigned i = 0; i < cells_.size(); i++) {
+ tempea = cell_attrs_[i];
+ if (GETBLINK(tempea)) {
+ blink = true;
+ break;
+ }
+ }
+
+ return blink;
+}
+
+void FQTermTextLine::setDirtyFlag(unsigned cell_begin, unsigned cell_end) {
+#ifdef TEXT_LINE_CHECK
+ FQ_VERIFY(cell_begin <= cell_end);
+ FQ_VERIFY(cell_end <= max_cell_count_);
+#endif
+ if (dirty_cell_begin_ > cell_begin) {
+ dirty_cell_begin_ = cell_begin;
+ }
+
+ if (dirty_cell_end_ < cell_end) {
+ dirty_cell_end_ = cell_end;
+ }
+}
+
+void FQTermTextLine::safelySetDirtyFlag(unsigned cell_begin, unsigned cell_end) {
+ if (cell_end > max_cell_count_) cell_end = max_cell_count_;
+
+ if (cell_begin >= cell_end)
+ return;
+
+ setDirtyFlag(cell_begin, cell_end);
+}
+
+void FQTermTextLine::clearDirtyFlag() {
+ dirty_cell_begin_ = 0;
+ dirty_cell_end_ = 0;
+}
+
+bool FQTermTextLine::getDirtyCells(unsigned &cell_begin, unsigned &cell_end) const {
+ cell_begin = dirty_cell_begin_;
+ cell_end = dirty_cell_end_;
+ return dirty_cell_begin_ < dirty_cell_end_;
+}
+
+void FQTermTextLine::getPlainText(unsigned cell_begin, unsigned cell_end,
+ QString &result) const {
+ verifyCellRange(cell_begin, cell_end);
+
+ if (cells_.size() == 0 || cell_begin == cell_end) return;
+
+ unsigned char_begin = getCharBegin(cell_begin);
+ unsigned char_end = getCharEnd(cell_end);
+
+ result.insert(result.size(),
+ (QChar *) &chars_[char_begin],
+ char_end - char_begin);
+
+ typedef char Verify_QChar_Is_Two_Bytes[
+ (sizeof(QChar) == 2)? 1 : -1];
+}
+
+void FQTermTextLine::getAnsiText(unsigned cell_begin, unsigned cell_end,
+ QString &result,
+ const char *escape) const {
+ verifyCellRange(cell_begin, cell_end);
+
+ if (cells_.size() == 0 || cell_begin == cell_end) return;
+
+ unsigned cur_cell = cell_begin;
+ unsigned next_cell = cell_begin;
+
+ while (cur_cell < cell_end) {
+ unsigned char color = cell_colors_[cur_cell];
+ unsigned char attr = cell_attrs_[cur_cell];
+
+ for (next_cell = cur_cell;
+ next_cell < cell_end;
+ ++next_cell) {
+ if (!(cell_colors_[next_cell] == color
+ && cell_attrs_[next_cell] == attr)) {
+ if (next_cell > getCellBegin(next_cell)) {
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ }
+
+ getAnsiCtrlSeq(color, attr, escape, result);
+ getPlainText(cur_cell, next_cell, result);
+ result += escape;
+ result += "0m";
+
+ cur_cell = next_cell;
+ }
+}
+
+
+void FQTermTextLine::appendWhiteSpace(
+ unsigned count, unsigned char color, unsigned char attr, UTF16 space) {
+ FQ_ASSERT(get_str_width(space) == 1);
+
+ cells_.reserve(getWidth() + count);
+ max_cell_count_ = qMax(max_cell_count_, getWidth() + count);
+
+ cell_colors_.reserve(getWidth() + count);
+ cell_attrs_.reserve(getWidth() + count);
+
+ setDirtyFlag(getWidth(), getWidth() + count);
+
+ chars_.insert(chars_.end(), count, space);
+ cell_colors_.insert(cell_colors_.end(), count, color);
+ cell_attrs_.insert(cell_attrs_.end(), count, attr);
+
+ int c = getCharEnd(getWidth());
+ for (unsigned i = 0; i < count; ++i) {
+ cells_.push_back(c + i + 1);
+ }
+
+ verifyTextLine();
+}
+
+void FQTermTextLine::insertWhiteSpace(unsigned count, unsigned cell_begin,
+ unsigned char color, unsigned char attr,
+ UTF16 space) {
+ FQ_ASSERT(get_str_width(space) == 1);
+
+ breakCell(cell_begin);
+
+ verifyCellRange(cell_begin, getWidth());
+
+ unsigned char_begin = getCharBegin(cell_begin);
+
+ max_cell_count_ = qMax(max_cell_count_, getWidth() + count);
+
+ chars_.insert(chars_.begin() + char_begin, count, space);
+ cell_colors_.insert(cell_colors_.begin() + cell_begin, count, color);
+ cell_attrs_.insert(cell_attrs_.begin() + cell_begin, count, attr);
+
+ cells_.insert(cells_.begin() + cell_begin, count, 0);
+ for (unsigned i = cell_begin; i < cell_begin + count; ++i) {
+ cells_[i] = char_begin + (i - cell_begin) + 1;
+ }
+ for (unsigned i = cell_begin + count; i < getWidth(); ++i) {
+ cells_[i] += count;
+ }
+
+ setDirtyFlag(cell_begin, this->getWidth());
+
+ verifyTextLine();
+}
+
+void FQTermTextLine::replaceWithWhiteSpace(
+ unsigned count, unsigned cell_begin, unsigned cell_end,
+ unsigned char color, unsigned char attr, UTF16 space) {
+ if(cell_end>cells_.size() || cell_begin>=cell_end)
+ return;
+ deleteText(cell_begin, cell_end);
+ insertWhiteSpace(count, cell_begin, color, attr, space);
+}
+
+
+
+void FQTermTextLine::insertText(const UTF16 *str, unsigned count,
+ unsigned cell_begin,
+ unsigned char color, unsigned char attr, int charstate) {
+ if (count == 0) return;
+
+ // Calculate how many cells should be occupied for str.
+ // TODO: avoid compute the width of str twice.
+ int width = get_str_width(str, count);
+
+ if (width == 0) {
+#ifdef TEXT_LINE_CHECK
+ //FQ_VERIFY(false);
+ //TODO: enable utf8 decoding
+#endif
+ return;
+ }
+
+ if (charstate & FQTermTextLine::FIRSTPART) {
+ stored_color_ = color;
+ stored_attr_ = attr;
+ width--;
+ count--;
+ if (width == 0 || count == 0) {
+ return;
+ }
+ }
+
+ breakCell(cell_begin);
+
+ verifyCellRange(cell_begin, getWidth());
+
+ unsigned char_begin = getCharBegin(cell_begin);
+
+ chars_.insert(chars_.begin() + char_begin, str, str + count);
+
+ // Insert colors.
+ cell_colors_.insert(cell_colors_.begin() + cell_begin, width, color);
+
+ // Insert attrs.
+ cell_attrs_.insert(cell_attrs_.begin() + cell_begin, width, attr);
+
+ if (charstate & FQTermTextLine::SECONDPART){
+ cell_colors_[cell_begin] = stored_color_;
+ cell_attrs_[cell_begin] = stored_attr_;
+ }
+
+
+ // Change the cells_ mapping to chars_.
+ cells_.insert(cells_.begin() + cell_begin, width, 0);
+ max_cell_count_ = qMax(max_cell_count_, getWidth());
+
+ unsigned last_cell = cell_begin;
+ unsigned cur_cell = last_cell;
+ for (const UTF16 *p = str; p < str +count;) {
+ int w = mk_advace_at_least_one_cell(&p, str + count);
+
+#ifdef TEXT_LINE_CHECK
+ // TODO_UTF16: FIXME: deal with decoding error.
+ FQ_VERIFY(w >= 0);
+#endif
+
+ cur_cell += w;
+
+ for (unsigned i = last_cell; i < cur_cell; ++i) {
+ cells_[i] = char_begin + (p - str);
+ }
+
+ last_cell = cur_cell;
+ }
+
+#ifdef TEXT_LINE_CHECK
+ FQ_VERIFY(cur_cell == cell_begin + width);
+#endif
+
+ for (unsigned i = cell_begin + width; i < cells_.size(); ++i) {
+ cells_[i] += count;
+ }
+
+
+
+ setDirtyFlag(cell_begin, cells_.size());
+
+ verifyTextLine();
+}
+
+void FQTermTextLine::deleteText(unsigned cell_begin, unsigned cell_end) {
+ if (cells_.size() == 0 || cell_begin == cell_end) return;
+
+ breakCell(cell_begin);
+ breakCell(cell_end);
+
+ verifyCellRange(cell_begin, cell_end);
+
+ setDirtyFlag(cell_begin, cells_.size());
+
+ unsigned char_begin = getCharBegin(cell_begin);
+ unsigned char_end = getCharEnd(cell_end);
+
+ chars_.erase(chars_.begin() + char_begin, chars_.begin() + char_end);
+
+ cells_.erase(cells_.begin() + cell_begin, cells_.begin() + cell_end);
+ cell_colors_.erase(cell_colors_.begin() + cell_begin,
+ cell_colors_.begin() + cell_end);
+ cell_attrs_.erase(cell_attrs_.begin() + cell_begin,
+ cell_attrs_.begin() + cell_end);
+
+ for (unsigned i = cell_begin; i < cells_.size(); ++i) {
+ cells_[i] -= (char_end - char_begin);
+ }
+
+ verifyTextLine();
+}
+
+void FQTermTextLine::deleteAllText() {
+ deleteText(0, getWidth());
+}
+
+void FQTermTextLine::replaceText(const UTF16 *str, unsigned count,
+ unsigned cell_begin, unsigned cell_end,
+ unsigned char color, unsigned char attr, int charstate) {
+ if (charstate & FQTermTextLine::FIRSTPART) {
+ stored_color_ = color;
+ stored_attr_ = attr;
+ count--;
+ if (count == 0)
+ {
+ return;
+ }
+ charstate &= ~FQTermTextLine::FIRSTPART;
+ }
+
+ deleteText(cell_begin, cell_end);
+ insertText(str, count, cell_begin, color, attr, charstate);
+
+ setDirtyFlag(cell_begin, cells_.size());
+}
+
+
+unsigned FQTermTextLine::getCellBegin(unsigned cell_begin) const {
+#ifdef TEXT_LINE_CHECK
+ FQ_VERIFY(cell_begin <= getWidth());
+#endif
+
+ if (cell_begin == cells_.size()) return cell_begin;
+
+ while(cell_begin > 0 && cells_[cell_begin - 1] == cells_[cell_begin]) {
+ --cell_begin;
+ }
+
+ return cell_begin;
+}
+
+unsigned FQTermTextLine::getCellEnd(unsigned cell_end) const {
+#ifdef TEXT_LINE_CHECK
+ FQ_VERIFY(cell_end <= getWidth());
+#endif
+
+ if (cell_end == 0) return cell_end;
+
+ while (cell_end < cells_.size() && cells_[cell_end - 1] == cells_[cell_end]) {
+ ++cell_end;
+ }
+
+ return cell_end;
+}
+
+unsigned FQTermTextLine::getCharBegin(unsigned cell_begin) const {
+#ifdef TEXT_LINE_CHECK
+ FQ_VERIFY(cell_begin == getCellBegin(cell_begin));
+#endif
+
+ if (cell_begin == 0) return 0;
+
+ return cells_[cell_begin - 1];
+}
+
+unsigned FQTermTextLine::getCharEnd(unsigned cell_end) const {
+#ifdef TEXT_LINE_CHECK
+ FQ_VERIFY(cell_end == getCellEnd(cell_end));
+#endif
+
+ if (cell_end == 0) return 0;
+
+ return cells_[cell_end - 1];
+}
+
+bool FQTermTextLine::breakCell(unsigned cell_begin) {
+ unsigned real_cell_begin = getCellBegin(cell_begin);
+ if (real_cell_begin == cell_begin) return false;
+
+ unsigned real_cell_end = getCellEnd(cell_begin);
+
+ verifyCellRange(real_cell_begin, real_cell_end);
+
+ unsigned cell_count = real_cell_end - real_cell_begin;
+
+ replaceWithWhiteSpace(cell_count, real_cell_begin, real_cell_end,
+ cell_colors_[cell_begin], cell_attrs_[cell_begin], URC);
+
+ return true;
+}
+
+void FQTermTextLine::getAnsiCtrlSeq(
+ unsigned char color, unsigned char attr,
+ const char *escape, QString &result) const {
+
+ int fg = GETFG(color) + 30;
+ int bg = GETBG(color) + 40;
+
+ result += escape;
+ result += QString("%1;%2").arg(fg).arg(bg);
+
+ if (GETBRIGHT(attr)) {
+ result += ";1";
+ }
+ if (GETDIM(attr)) {
+ result += ";2";
+ }
+ if (GETUNDERLINE(attr)) {
+ result += ";4";
+ }
+ if (GETBLINK(attr)) {
+ result += ";5";
+ }
+ if (GETRAPIDBLINK(attr)) {
+ result += ";6";
+ }
+ if (GETREVERSE(attr)) {
+ result += ";7";
+ }
+ if (GETINVISIBLE(attr)) {
+ result += ";8";
+ }
+
+ result += "m";
+}
+
+
+} // namespace FQTerm