diff options
Diffstat (limited to 'qtermwidget/Screen.cpp')
-rw-r--r-- | qtermwidget/Screen.cpp | 1567 |
1 files changed, 1567 insertions, 0 deletions
diff --git a/qtermwidget/Screen.cpp b/qtermwidget/Screen.cpp new file mode 100644 index 0000000..ead0066 --- /dev/null +++ b/qtermwidget/Screen.cpp @@ -0,0 +1,1567 @@ +/* + This file is part of Konsole, an X terminal. + Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + + Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 + + 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 Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "Screen.h" + +// Standard +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <assert.h> +#include <string.h> +#include <ctype.h> + +// Qt +#include <QtCore/QTextStream> +#include <QtCore/QDate> + +// Konsole +#include "konsole_wcwidth.h" +#include "TerminalCharacterDecoder.h" + +using namespace Konsole; + +//FIXME: this is emulation specific. Use false for xterm, true for ANSI. +//FIXME: see if we can get this from terminfo. +#define BS_CLEARS false + +//Macro to convert x,y position on screen to position within an image. +// +//Originally the image was stored as one large contiguous block of +//memory, so a position within the image could be represented as an +//offset from the beginning of the block. For efficiency reasons this +//is no longer the case. +//Many internal parts of this class still use this representation for parameters and so on, +//notably moveImage() and clearImage(). +//This macro converts from an X,Y position into an image offset. +#ifndef loc +#define loc(X,Y) ((Y)*columns+(X)) +#endif + + +Character Screen::defaultChar = Character(' ', + CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR), + CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR), + DEFAULT_RENDITION); + +//#define REVERSE_WRAPPED_LINES // for wrapped line debug + +Screen::Screen(int l, int c) + : lines(l), + columns(c), + screenLines(new ImageLine[lines+1] ), + _scrolledLines(0), + _droppedLines(0), + hist(new HistoryScrollNone()), + cuX(0), cuY(0), + cu_re(0), + tmargin(0), bmargin(0), + tabstops(0), + sel_begin(0), sel_TL(0), sel_BR(0), + sel_busy(false), + columnmode(false), + ef_fg(CharacterColor()), ef_bg(CharacterColor()), ef_re(0), + sa_cuX(0), sa_cuY(0), + sa_cu_re(0), + lastPos(-1) +{ + lineProperties.resize(lines+1); + for (int i=0;i<lines+1;i++) + lineProperties[i]=LINE_DEFAULT; + + initTabStops(); + clearSelection(); + reset(); +} + +/*! Destructor +*/ + +Screen::~Screen() +{ + delete[] screenLines; + delete[] tabstops; + delete hist; +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Normalized Screen Operations */ +/* */ +/* ------------------------------------------------------------------------- */ + +// Cursor Setting -------------------------------------------------------------- + +/*! \section Cursor + + The `cursor' is a location within the screen that is implicitely used in + many operations. The operations within this section allow to manipulate + the cursor explicitly and to obtain it's value. + + The position of the cursor is guarantied to be between (including) 0 and + `columns-1' and `lines-1'. +*/ + +/*! + Move the cursor up. + + The cursor will not be moved beyond the top margin. +*/ + +void Screen::cursorUp(int n) +//=CUU +{ + if (n == 0) n = 1; // Default + int stop = cuY < tmargin ? 0 : tmargin; + cuX = qMin(columns-1,cuX); // nowrap! + cuY = qMax(stop,cuY-n); +} + +/*! + Move the cursor down. + + The cursor will not be moved beyond the bottom margin. +*/ + +void Screen::cursorDown(int n) +//=CUD +{ + if (n == 0) n = 1; // Default + int stop = cuY > bmargin ? lines-1 : bmargin; + cuX = qMin(columns-1,cuX); // nowrap! + cuY = qMin(stop,cuY+n); +} + +/*! + Move the cursor left. + + The cursor will not move beyond the first column. +*/ + +void Screen::cursorLeft(int n) +//=CUB +{ + if (n == 0) n = 1; // Default + cuX = qMin(columns-1,cuX); // nowrap! + cuX = qMax(0,cuX-n); +} + +/*! + Move the cursor left. + + The cursor will not move beyond the rightmost column. +*/ + +void Screen::cursorRight(int n) +//=CUF +{ + if (n == 0) n = 1; // Default + cuX = qMin(columns-1,cuX+n); +} + +void Screen::setMargins(int top, int bot) +//=STBM +{ + if (top == 0) top = 1; // Default + if (bot == 0) bot = lines; // Default + top = top - 1; // Adjust to internal lineno + bot = bot - 1; // Adjust to internal lineno + if ( !( 0 <= top && top < bot && bot < lines ) ) + { qDebug()<<" setRegion("<<top<<","<<bot<<") : bad range."; + return; // Default error action: ignore + } + tmargin = top; + bmargin = bot; + cuX = 0; + cuY = getMode(MODE_Origin) ? top : 0; + +} + +int Screen::topMargin() const +{ + return tmargin; +} +int Screen::bottomMargin() const +{ + return bmargin; +} + +void Screen::index() +//=IND +{ + if (cuY == bmargin) + { + scrollUp(1); + } + else if (cuY < lines-1) + cuY += 1; +} + +void Screen::reverseIndex() +//=RI +{ + if (cuY == tmargin) + scrollDown(tmargin,1); + else if (cuY > 0) + cuY -= 1; +} + +/*! + Move the cursor to the begin of the next line. + + If cursor is on bottom margin, the region between the + actual top and bottom margin is scrolled up. +*/ + +void Screen::NextLine() +//=NEL +{ + Return(); index(); +} + +void Screen::eraseChars(int n) +{ + if (n == 0) n = 1; // Default + int p = qMax(0,qMin(cuX+n-1,columns-1)); + clearImage(loc(cuX,cuY),loc(p,cuY),' '); +} + +void Screen::deleteChars(int n) +{ + Q_ASSERT( n >= 0 ); + + // always delete at least one char + if (n == 0) + n = 1; + + // if cursor is beyond the end of the line there is nothing to do + if ( cuX >= screenLines[cuY].count() ) + return; + + if ( cuX+n >= screenLines[cuY].count() ) + n = screenLines[cuY].count() - 1 - cuX; + + Q_ASSERT( n >= 0 ); + Q_ASSERT( cuX+n < screenLines[cuY].count() ); + + screenLines[cuY].remove(cuX,n); +} + +void Screen::insertChars(int n) +{ + if (n == 0) n = 1; // Default + + if ( screenLines[cuY].size() < cuX ) + screenLines[cuY].resize(cuX); + + screenLines[cuY].insert(cuX,n,' '); + + if ( screenLines[cuY].count() > columns ) + screenLines[cuY].resize(columns); +} + +void Screen::deleteLines(int n) +{ + if (n == 0) n = 1; // Default + scrollUp(cuY,n); +} + +/*! insert `n' lines at the cursor position. + + The cursor is not moved by the operation. +*/ + +void Screen::insertLines(int n) +{ + if (n == 0) n = 1; // Default + scrollDown(cuY,n); +} + +// Mode Operations ----------------------------------------------------------- + +/*! Set a specific mode. */ + +void Screen::setMode(int m) +{ + currParm.mode[m] = true; + switch(m) + { + case MODE_Origin : cuX = 0; cuY = tmargin; break; //FIXME: home + } +} + +/*! Reset a specific mode. */ + +void Screen::resetMode(int m) +{ + currParm.mode[m] = false; + switch(m) + { + case MODE_Origin : cuX = 0; cuY = 0; break; //FIXME: home + } +} + +/*! Save a specific mode. */ + +void Screen::saveMode(int m) +{ + saveParm.mode[m] = currParm.mode[m]; +} + +/*! Restore a specific mode. */ + +void Screen::restoreMode(int m) +{ + currParm.mode[m] = saveParm.mode[m]; +} + +bool Screen::getMode(int m) const +{ + return currParm.mode[m]; +} + +void Screen::saveCursor() +{ + sa_cuX = cuX; + sa_cuY = cuY; + sa_cu_re = cu_re; + sa_cu_fg = cu_fg; + sa_cu_bg = cu_bg; +} + +void Screen::restoreCursor() +{ + cuX = qMin(sa_cuX,columns-1); + cuY = qMin(sa_cuY,lines-1); + cu_re = sa_cu_re; + cu_fg = sa_cu_fg; + cu_bg = sa_cu_bg; + effectiveRendition(); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Screen Operations */ +/* */ +/* ------------------------------------------------------------------------- */ + +/*! Resize the screen image + + The topmost left position is maintained, while lower lines + or right hand side columns might be removed or filled with + spaces to fit the new size. + + The region setting is reset to the whole screen and the + tab positions reinitialized. + + If the new image is narrower than the old image then text on lines + which extends past the end of the new image is preserved so that it becomes + visible again if the screen is later resized to make it larger. +*/ + +void Screen::resizeImage(int new_lines, int new_columns) +{ + if ((new_lines==lines) && (new_columns==columns)) return; + + if (cuY > new_lines-1) + { // attempt to preserve focus and lines + bmargin = lines-1; //FIXME: margin lost + for (int i = 0; i < cuY-(new_lines-1); i++) + { + addHistLine(); scrollUp(0,1); + } + } + + // create new screen lines and copy from old to new + + ImageLine* newScreenLines = new ImageLine[new_lines+1]; + for (int i=0; i < qMin(lines-1,new_lines+1) ;i++) + newScreenLines[i]=screenLines[i]; + for (int i=lines;(i > 0) && (i<new_lines+1);i++) + newScreenLines[i].resize( new_columns ); + + lineProperties.resize(new_lines+1); + for (int i=lines;(i > 0) && (i<new_lines+1);i++) + lineProperties[i] = LINE_DEFAULT; + + clearSelection(); + + delete[] screenLines; + screenLines = newScreenLines; + + lines = new_lines; + columns = new_columns; + cuX = qMin(cuX,columns-1); + cuY = qMin(cuY,lines-1); + + // FIXME: try to keep values, evtl. + tmargin=0; + bmargin=lines-1; + initTabStops(); + clearSelection(); +} + +void Screen::setDefaultMargins() +{ + tmargin = 0; + bmargin = lines-1; +} + + +/* + Clarifying rendition here and in the display. + + currently, the display's color table is + 0 1 2 .. 9 10 .. 17 + dft_fg, dft_bg, dim 0..7, intensive 0..7 + + cu_fg, cu_bg contain values 0..8; + - 0 = default color + - 1..8 = ansi specified color + + re_fg, re_bg contain values 0..17 + due to the TerminalDisplay's color table + + rendition attributes are + + attr widget screen + -------------- ------ ------ + RE_UNDERLINE XX XX affects foreground only + RE_BLINK XX XX affects foreground only + RE_BOLD XX XX affects foreground only + RE_REVERSE -- XX + RE_TRANSPARENT XX -- affects background only + RE_INTENSIVE XX -- affects foreground only + + Note that RE_BOLD is used in both widget + and screen rendition. Since xterm/vt102 + is to poor to distinguish between bold + (which is a font attribute) and intensive + (which is a color attribute), we translate + this and RE_BOLD in falls eventually appart + into RE_BOLD and RE_INTENSIVE. +*/ + +void Screen::reverseRendition(Character& p) const +{ + CharacterColor f = p.foregroundColor; + CharacterColor b = p.backgroundColor; + + p.foregroundColor = b; + p.backgroundColor = f; //p->r &= ~RE_TRANSPARENT; +} + +void Screen::effectiveRendition() +// calculate rendition +{ + //copy "current rendition" straight into "effective rendition", which is then later copied directly + //into the image[] array which holds the characters and their appearance properties. + //- The old version below filtered out all attributes other than underline and blink at this stage, + //so that they would not be copied into the image[] array and hence would not be visible by TerminalDisplay + //which actually paints the screen using the information from the image[] array. + //I don't know why it did this, but I'm fairly sure it was the wrong thing to do. The net result + //was that bold text wasn't printed in bold by Konsole. + ef_re = cu_re; + + //OLD VERSION: + //ef_re = cu_re & (RE_UNDERLINE | RE_BLINK); + + if (cu_re & RE_REVERSE) + { + ef_fg = cu_bg; + ef_bg = cu_fg; + } + else + { + ef_fg = cu_fg; + ef_bg = cu_bg; + } + + if (cu_re & RE_BOLD) + ef_fg.toggleIntensive(); +} + +/*! + returns the image. + + Get the size of the image by \sa getLines and \sa getColumns. + + NOTE that the image returned by this function must later be + freed. + +*/ + +void Screen::copyFromHistory(Character* dest, int startLine, int count) const +{ + Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= hist->getLines() ); + + for (int line = startLine; line < startLine + count; line++) + { + const int length = qMin(columns,hist->getLineLen(line)); + const int destLineOffset = (line-startLine)*columns; + + hist->getCells(line,0,length,dest + destLineOffset); + + for (int column = length; column < columns; column++) + dest[destLineOffset+column] = defaultChar; + + // invert selected text + if (sel_begin !=-1) + { + for (int column = 0; column < columns; column++) + { + if (isSelected(column,line)) + { + reverseRendition(dest[destLineOffset + column]); + } + } + } + } +} + +void Screen::copyFromScreen(Character* dest , int startLine , int count) const +{ + Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= lines ); + + for (int line = startLine; line < (startLine+count) ; line++) + { + int srcLineStartIndex = line*columns; + int destLineStartIndex = (line-startLine)*columns; + + for (int column = 0; column < columns; column++) + { + int srcIndex = srcLineStartIndex + column; + int destIndex = destLineStartIndex + column; + + dest[destIndex] = screenLines[srcIndex/columns].value(srcIndex%columns,defaultChar); + + // invert selected text + if (sel_begin != -1 && isSelected(column,line + hist->getLines())) + reverseRendition(dest[destIndex]); + } + + } +} + +void Screen::getImage( Character* dest, int size, int startLine, int endLine ) const +{ + Q_ASSERT( startLine >= 0 ); + Q_ASSERT( endLine >= startLine && endLine < hist->getLines() + lines ); + + const int mergedLines = endLine - startLine + 1; + + Q_ASSERT( size >= mergedLines * columns ); + + const int linesInHistoryBuffer = qBound(0,hist->getLines()-startLine,mergedLines); + const int linesInScreenBuffer = mergedLines - linesInHistoryBuffer; + + // copy lines from history buffer + if (linesInHistoryBuffer > 0) { + copyFromHistory(dest,startLine,linesInHistoryBuffer); + } + + // copy lines from screen buffer + if (linesInScreenBuffer > 0) { + copyFromScreen(dest + linesInHistoryBuffer*columns, + startLine + linesInHistoryBuffer - hist->getLines(), + linesInScreenBuffer); + } + + // invert display when in screen mode + if (getMode(MODE_Screen)) + { + for (int i = 0; i < mergedLines*columns; i++) + reverseRendition(dest[i]); // for reverse display + } + + // mark the character at the current cursor position + int cursorIndex = loc(cuX, cuY + linesInHistoryBuffer); + if(getMode(MODE_Cursor) && cursorIndex < columns*mergedLines) + dest[cursorIndex].rendition |= RE_CURSOR; +} + +QVector<LineProperty> Screen::getLineProperties( int startLine , int endLine ) const +{ + Q_ASSERT( startLine >= 0 ); + Q_ASSERT( endLine >= startLine && endLine < hist->getLines() + lines ); + + const int mergedLines = endLine-startLine+1; + const int linesInHistory = qBound(0,hist->getLines()-startLine,mergedLines); + const int linesInScreen = mergedLines - linesInHistory; + + QVector<LineProperty> result(mergedLines); + int index = 0; + + // copy properties for lines in history + for (int line = startLine; line < startLine + linesInHistory; line++) + { + //TODO Support for line properties other than wrapped lines + if (hist->isWrappedLine(line)) + { + result[index] = (LineProperty)(result[index] | LINE_WRAPPED); + } + index++; + } + + // copy properties for lines in screen buffer + const int firstScreenLine = startLine + linesInHistory - hist->getLines(); + for (int line = firstScreenLine; line < firstScreenLine+linesInScreen; line++) + { + result[index]=lineProperties[line]; + index++; + } + + return result; +} + +/*! +*/ + +void Screen::reset(bool clearScreen) +{ + setMode(MODE_Wrap ); saveMode(MODE_Wrap ); // wrap at end of margin + resetMode(MODE_Origin); saveMode(MODE_Origin); // position refere to [1,1] + resetMode(MODE_Insert); saveMode(MODE_Insert); // overstroke + setMode(MODE_Cursor); // cursor visible + resetMode(MODE_Screen); // screen not inverse + resetMode(MODE_NewLine); + + tmargin=0; + bmargin=lines-1; + + setDefaultRendition(); + saveCursor(); + + if ( clearScreen ) + clear(); +} + +/*! Clear the entire screen and home the cursor. +*/ + +void Screen::clear() +{ + clearEntireScreen(); + home(); +} + +void Screen::BackSpace() +{ + cuX = qMin(columns-1,cuX); // nowrap! + cuX = qMax(0,cuX-1); + // if (BS_CLEARS) image[loc(cuX,cuY)].character = ' '; + + if (screenLines[cuY].size() < cuX+1) + screenLines[cuY].resize(cuX+1); + + if (BS_CLEARS) screenLines[cuY][cuX].character = ' '; +} + +void Screen::Tabulate(int n) +{ + // note that TAB is a format effector (does not write ' '); + if (n == 0) n = 1; + while((n > 0) && (cuX < columns-1)) + { + cursorRight(1); while((cuX < columns-1) && !tabstops[cuX]) cursorRight(1); + n--; + } +} + +void Screen::backTabulate(int n) +{ + // note that TAB is a format effector (does not write ' '); + if (n == 0) n = 1; + while((n > 0) && (cuX > 0)) + { + cursorLeft(1); while((cuX > 0) && !tabstops[cuX]) cursorLeft(1); + n--; + } +} + +void Screen::clearTabStops() +{ + for (int i = 0; i < columns; i++) tabstops[i] = false; +} + +void Screen::changeTabStop(bool set) +{ + if (cuX >= columns) return; + tabstops[cuX] = set; +} + +void Screen::initTabStops() +{ + delete[] tabstops; + tabstops = new bool[columns]; + + // Arrg! The 1st tabstop has to be one longer than the other. + // i.e. the kids start counting from 0 instead of 1. + // Other programs might behave correctly. Be aware. + for (int i = 0; i < columns; i++) tabstops[i] = (i%8 == 0 && i != 0); +} + +/*! + This behaves either as IND (Screen::Index) or as NEL (Screen::NextLine) + depending on the NewLine Mode (LNM). This mode also + affects the key sequence returned for newline ([CR]LF). +*/ + +void Screen::NewLine() +{ + if (getMode(MODE_NewLine)) Return(); + index(); +} + +/*! put `c' literally onto the screen at the current cursor position. + + VT100 uses the convention to produce an automatic newline (am) + with the *first* character that would fall onto the next line (xenl). +*/ + +void Screen::checkSelection(int from, int to) +{ + if (sel_begin == -1) return; + int scr_TL = loc(0, hist->getLines()); + //Clear entire selection if it overlaps region [from, to] + if ( (sel_BR > (from+scr_TL) )&&(sel_TL < (to+scr_TL)) ) + { + clearSelection(); + } +} + +void Screen::ShowCharacter(unsigned short c) +{ + // Note that VT100 does wrapping BEFORE putting the character. + // This has impact on the assumption of valid cursor positions. + // We indicate the fact that a newline has to be triggered by + // putting the cursor one right to the last column of the screen. + + int w = konsole_wcwidth(c); + + if (w <= 0) + return; + + if (cuX+w > columns) { + if (getMode(MODE_Wrap)) { + lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | LINE_WRAPPED); + NextLine(); + } + else + cuX = columns-w; + } + + // ensure current line vector has enough elements + int size = screenLines[cuY].size(); + if (size == 0 && cuY > 0) + { + screenLines[cuY].resize( qMax(screenLines[cuY-1].size() , cuX+w) ); + } + else + { + if (size < cuX+w) + { + screenLines[cuY].resize(cuX+w); + } + } + + if (getMode(MODE_Insert)) insertChars(w); + + lastPos = loc(cuX,cuY); + + // check if selection is still valid. + checkSelection(cuX,cuY); + + Character& currentChar = screenLines[cuY][cuX]; + + currentChar.character = c; + currentChar.foregroundColor = ef_fg; + currentChar.backgroundColor = ef_bg; + currentChar.rendition = ef_re; + + int i = 0; + int newCursorX = cuX + w--; + while(w) + { + i++; + + if ( screenLines[cuY].size() < cuX + i + 1 ) + screenLines[cuY].resize(cuX+i+1); + + Character& ch = screenLines[cuY][cuX + i]; + ch.character = 0; + ch.foregroundColor = ef_fg; + ch.backgroundColor = ef_bg; + ch.rendition = ef_re; + + w--; + } + cuX = newCursorX; +} + +void Screen::compose(const QString& /*compose*/) +{ + Q_ASSERT( 0 /*Not implemented yet*/ ); + +/* if (lastPos == -1) + return; + + QChar c(image[lastPos].character); + compose.prepend(c); + //compose.compose(); ### FIXME! + image[lastPos].character = compose[0].unicode();*/ +} + +int Screen::scrolledLines() const +{ + return _scrolledLines; +} +int Screen::droppedLines() const +{ + return _droppedLines; +} +void Screen::resetDroppedLines() +{ + _droppedLines = 0; +} +void Screen::resetScrolledLines() +{ + //kDebug() << "scrolled lines reset"; + + _scrolledLines = 0; +} + +// Region commands ------------------------------------------------------------- + +void Screen::scrollUp(int n) +{ + if (n == 0) n = 1; // Default + if (tmargin == 0) addHistLine(); // hist.history + scrollUp(tmargin, n); +} + +/*! scroll up `n' lines within current region. + The `n' new lines are cleared. + \sa setRegion \sa scrollDown +*/ + +QRect Screen::lastScrolledRegion() const +{ + return _lastScrolledRegion; +} + +void Screen::scrollUp(int from, int n) +{ + if (n <= 0 || from + n > bmargin) return; + + _scrolledLines -= n; + _lastScrolledRegion = QRect(0,tmargin,columns-1,(bmargin-tmargin)); + + //FIXME: make sure `tmargin', `bmargin', `from', `n' is in bounds. + moveImage(loc(0,from),loc(0,from+n),loc(columns-1,bmargin)); + clearImage(loc(0,bmargin-n+1),loc(columns-1,bmargin),' '); +} + +void Screen::scrollDown(int n) +{ + if (n == 0) n = 1; // Default + scrollDown(tmargin, n); +} + +/*! scroll down `n' lines within current region. + The `n' new lines are cleared. + \sa setRegion \sa scrollUp +*/ + +void Screen::scrollDown(int from, int n) +{ + + //kDebug() << "Screen::scrollDown( from: " << from << " , n: " << n << ")"; + + _scrolledLines += n; + +//FIXME: make sure `tmargin', `bmargin', `from', `n' is in bounds. + if (n <= 0) return; + if (from > bmargin) return; + if (from + n > bmargin) n = bmargin - from; + moveImage(loc(0,from+n),loc(0,from),loc(columns-1,bmargin-n)); + clearImage(loc(0,from),loc(columns-1,from+n-1),' '); +} + +void Screen::setCursorYX(int y, int x) +{ + setCursorY(y); setCursorX(x); +} + +void Screen::setCursorX(int x) +{ + if (x == 0) x = 1; // Default + x -= 1; // Adjust + cuX = qMax(0,qMin(columns-1, x)); +} + +void Screen::setCursorY(int y) +{ + if (y == 0) y = 1; // Default + y -= 1; // Adjust + cuY = qMax(0,qMin(lines -1, y + (getMode(MODE_Origin) ? tmargin : 0) )); +} + +void Screen::home() +{ + cuX = 0; + cuY = 0; +} + +void Screen::Return() +{ + cuX = 0; +} + +int Screen::getCursorX() const +{ + return cuX; +} + +int Screen::getCursorY() const +{ + return cuY; +} + +// Erasing --------------------------------------------------------------------- + +/*! \section Erasing + + This group of operations erase parts of the screen contents by filling + it with spaces colored due to the current rendition settings. + + Althought the cursor position is involved in most of these operations, + it is never modified by them. +*/ + +/*! fill screen between (including) `loca' (start) and `loce' (end) with spaces. + + This is an internal helper functions. The parameter types are internal + addresses of within the screen image and make use of the way how the + screen matrix is mapped to the image vector. +*/ + +void Screen::clearImage(int loca, int loce, char c) +{ + int scr_TL=loc(0,hist->getLines()); + //FIXME: check positions + + //Clear entire selection if it overlaps region to be moved... + if ( (sel_BR > (loca+scr_TL) )&&(sel_TL < (loce+scr_TL)) ) + { + clearSelection(); + } + + int topLine = loca/columns; + int bottomLine = loce/columns; + + Character clearCh(c,cu_fg,cu_bg,DEFAULT_RENDITION); + + //if the character being used to clear the area is the same as the + //default character, the affected lines can simply be shrunk. + bool isDefaultCh = (clearCh == Character()); + + for (int y=topLine;y<=bottomLine;y++) + { + lineProperties[y] = 0; + + int endCol = ( y == bottomLine) ? loce%columns : columns-1; + int startCol = ( y == topLine ) ? loca%columns : 0; + + QVector<Character>& line = screenLines[y]; + + if ( isDefaultCh && endCol == columns-1 ) + { + line.resize(startCol); + } + else + { + if (line.size() < endCol + 1) + line.resize(endCol+1); + + Character* data = line.data(); + for (int i=startCol;i<=endCol;i++) + data[i]=clearCh; + } + } +} + +/*! move image between (including) `sourceBegin' and `sourceEnd' to 'dest'. + + The 'dest', 'sourceBegin' and 'sourceEnd' parameters can be generated using + the loc(column,line) macro. + +NOTE: moveImage() can only move whole lines. + + This is an internal helper functions. The parameter types are internal + addresses of within the screen image and make use of the way how the + screen matrix is mapped to the image vector. +*/ + +void Screen::moveImage(int dest, int sourceBegin, int sourceEnd) +{ + //kDebug() << "moving image from (" << (sourceBegin/columns) + // << "," << (sourceEnd/columns) << ") to " << + // (dest/columns); + + Q_ASSERT( sourceBegin <= sourceEnd ); + + int lines=(sourceEnd-sourceBegin)/columns; + + //move screen image and line properties: + //the source and destination areas of the image may overlap, + //so it matters that we do the copy in the right order - + //forwards if dest < sourceBegin or backwards otherwise. + //(search the web for 'memmove implementation' for details) + if (dest < sourceBegin) + { + for (int i=0;i<=lines;i++) + { + screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ]; + lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i]; + } + } + else + { + for (int i=lines;i>=0;i--) + { + screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ]; + lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i]; + } + } + + if (lastPos != -1) + { + int diff = dest - sourceBegin; // Scroll by this amount + lastPos += diff; + if ((lastPos < 0) || (lastPos >= (lines*columns))) + lastPos = -1; + } + + // Adjust selection to follow scroll. + if (sel_begin != -1) + { + bool beginIsTL = (sel_begin == sel_TL); + int diff = dest - sourceBegin; // Scroll by this amount + int scr_TL=loc(0,hist->getLines()); + int srca = sourceBegin+scr_TL; // Translate index from screen to global + int srce = sourceEnd+scr_TL; // Translate index from screen to global + int desta = srca+diff; + int deste = srce+diff; + + if ((sel_TL >= srca) && (sel_TL <= srce)) + sel_TL += diff; + else if ((sel_TL >= desta) && (sel_TL <= deste)) + sel_BR = -1; // Clear selection (see below) + + if ((sel_BR >= srca) && (sel_BR <= srce)) + sel_BR += diff; + else if ((sel_BR >= desta) && (sel_BR <= deste)) + sel_BR = -1; // Clear selection (see below) + + if (sel_BR < 0) + { + clearSelection(); + } + else + { + if (sel_TL < 0) + sel_TL = 0; + } + + if (beginIsTL) + sel_begin = sel_TL; + else + sel_begin = sel_BR; + } +} + +void Screen::clearToEndOfScreen() +{ + clearImage(loc(cuX,cuY),loc(columns-1,lines-1),' '); +} + +void Screen::clearToBeginOfScreen() +{ + clearImage(loc(0,0),loc(cuX,cuY),' '); +} + +void Screen::clearEntireScreen() +{ + // Add entire screen to history + for (int i = 0; i < (lines-1); i++) + { + addHistLine(); scrollUp(0,1); + } + + clearImage(loc(0,0),loc(columns-1,lines-1),' '); +} + +/*! fill screen with 'E' + This is to aid screen alignment +*/ + +void Screen::helpAlign() +{ + clearImage(loc(0,0),loc(columns-1,lines-1),'E'); +} + +void Screen::clearToEndOfLine() +{ + clearImage(loc(cuX,cuY),loc(columns-1,cuY),' '); +} + +void Screen::clearToBeginOfLine() +{ + clearImage(loc(0,cuY),loc(cuX,cuY),' '); +} + +void Screen::clearEntireLine() +{ + clearImage(loc(0,cuY),loc(columns-1,cuY),' '); +} + +void Screen::setRendition(int re) +{ + cu_re |= re; + effectiveRendition(); +} + +void Screen::resetRendition(int re) +{ + cu_re &= ~re; + effectiveRendition(); +} + +void Screen::setDefaultRendition() +{ + setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR); + setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR); + cu_re = DEFAULT_RENDITION; + effectiveRendition(); +} + +void Screen::setForeColor(int space, int color) +{ + cu_fg = CharacterColor(space, color); + + if ( cu_fg.isValid() ) + effectiveRendition(); + else + setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR); +} + +void Screen::setBackColor(int space, int color) +{ + cu_bg = CharacterColor(space, color); + + if ( cu_bg.isValid() ) + effectiveRendition(); + else + setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Marking & Selection */ +/* */ +/* ------------------------------------------------------------------------- */ + +void Screen::clearSelection() +{ + sel_BR = -1; + sel_TL = -1; + sel_begin = -1; +} + +void Screen::getSelectionStart(int& column , int& line) +{ + if ( sel_TL != -1 ) + { + column = sel_TL % columns; + line = sel_TL / columns; + } + else + { + column = cuX + getHistLines(); + line = cuY + getHistLines(); + } +} +void Screen::getSelectionEnd(int& column , int& line) +{ + if ( sel_BR != -1 ) + { + column = sel_BR % columns; + line = sel_BR / columns; + } + else + { + column = cuX + getHistLines(); + line = cuY + getHistLines(); + } +} +void Screen::setSelectionStart(/*const ScreenCursor& viewCursor ,*/ const int x, const int y, const bool mode) +{ +// kDebug(1211) << "setSelBeginXY(" << x << "," << y << ")"; + sel_begin = loc(x,y); //+histCursor) ; + + /* FIXME, HACK to correct for x too far to the right... */ + if (x == columns) sel_begin--; + + sel_BR = sel_begin; + sel_TL = sel_begin; + columnmode = mode; +} + +void Screen::setSelectionEnd( const int x, const int y) +{ +// kDebug(1211) << "setSelExtentXY(" << x << "," << y << ")"; + if (sel_begin == -1) return; + int l = loc(x,y); // + histCursor); + + if (l < sel_begin) + { + sel_TL = l; + sel_BR = sel_begin; + } + else + { + /* FIXME, HACK to correct for x too far to the right... */ + if (x == columns) l--; + + sel_TL = sel_begin; + sel_BR = l; + } +} + +bool Screen::isSelected( const int x,const int y) const +{ + if (columnmode) { + int sel_Left,sel_Right; + if ( sel_TL % columns < sel_BR % columns ) { + sel_Left = sel_TL; sel_Right = sel_BR; + } else { + sel_Left = sel_BR; sel_Right = sel_TL; + } + return ( x >= sel_Left % columns ) && ( x <= sel_Right % columns ) && + ( y >= sel_TL / columns ) && ( y <= sel_BR / columns ); + //( y+histCursor >= sel_TL / columns ) && ( y+histCursor <= sel_BR / columns ); + } + else { + //int pos = loc(x,y+histCursor); + int pos = loc(x,y); + return ( pos >= sel_TL && pos <= sel_BR ); + } +} + +QString Screen::selectedText(bool preserveLineBreaks) +{ + QString result; + QTextStream stream(&result, QIODevice::ReadWrite); + + PlainTextDecoder decoder; + decoder.begin(&stream); + writeSelectionToStream(&decoder , preserveLineBreaks); + decoder.end(); + + return result; +} + +bool Screen::isSelectionValid() const +{ + return ( sel_TL >= 0 && sel_BR >= 0 ); +} + +void Screen::writeSelectionToStream(TerminalCharacterDecoder* decoder , + bool preserveLineBreaks) +{ + // do nothing if selection is invalid + if ( !isSelectionValid() ) + return; + + int top = sel_TL / columns; + int left = sel_TL % columns; + + int bottom = sel_BR / columns; + int right = sel_BR % columns; + + Q_ASSERT( top >= 0 && left >= 0 && bottom >= 0 && right >= 0 ); + + //kDebug() << "sel_TL = " << sel_TL; + //kDebug() << "columns = " << columns; + + for (int y=top;y<=bottom;y++) + { + int start = 0; + if ( y == top || columnmode ) start = left; + + int count = -1; + if ( y == bottom || columnmode ) count = right - start + 1; + + const bool appendNewLine = ( y != bottom ); + copyLineToStream( y, + start, + count, + decoder, + appendNewLine, + preserveLineBreaks ); + } +} + + +void Screen::copyLineToStream(int line , + int start, + int count, + TerminalCharacterDecoder* decoder, + bool appendNewLine, + bool preserveLineBreaks) +{ + //buffer to hold characters for decoding + //the buffer is static to avoid initialising every + //element on each call to copyLineToStream + //(which is unnecessary since all elements will be overwritten anyway) + static const int MAX_CHARS = 1024; + static Character characterBuffer[MAX_CHARS]; + + assert( count < MAX_CHARS ); + + LineProperty currentLineProperties = 0; + + //determine if the line is in the history buffer or the screen image + if (line < hist->getLines()) + { + const int lineLength = hist->getLineLen(line); + + // ensure that start position is before end of line + start = qMin(start,qMax(0,lineLength-1)); + + //retrieve line from history buffer + if (count == -1) + { + count = lineLength-start; + } + else + { + count = qMin(start+count,lineLength)-start; + } + + // safety checks + assert( start >= 0 ); + assert( count >= 0 ); + assert( (start+count) <= hist->getLineLen(line) ); + + hist->getCells(line,start,count,characterBuffer); + + if ( hist->isWrappedLine(line) ) + currentLineProperties |= LINE_WRAPPED; + } + else + { + if ( count == -1 ) + count = columns - start; + + assert( count >= 0 ); + + const int screenLine = line-hist->getLines(); + + Character* data = screenLines[screenLine].data(); + int length = screenLines[screenLine].count(); + + //retrieve line from screen image + for (int i=start;i < qMin(start+count,length);i++) + { + characterBuffer[i-start] = data[i]; + } + + // count cannot be any greater than length + count = qBound(0,count,length-start); + + Q_ASSERT( screenLine < lineProperties.count() ); + currentLineProperties |= lineProperties[screenLine]; + } + + //do not decode trailing whitespace characters + for (int i=count-1 ; i >= 0; i--) + if (QChar(characterBuffer[i].character).isSpace()) + count--; + else + break; + + // add new line character at end + const bool omitLineBreak = (currentLineProperties & LINE_WRAPPED) || + !preserveLineBreaks; + + if ( !omitLineBreak && appendNewLine && (count+1 < MAX_CHARS) ) + { + characterBuffer[count] = '\n'; + count++; + } + + //decode line and write to text stream + decoder->decodeLine( (Character*) characterBuffer , + count, currentLineProperties ); +} + +// Method below has been removed because of its reliance on 'histCursor' +// and I want to restrict the methods which have knowledge of the scroll position +// to just those which deal with selection and supplying final screen images. +// +/*void Screen::writeToStream(QTextStream* stream , TerminalCharacterDecoder* decoder) { + sel_begin = 0; + sel_BR = sel_begin; + sel_TL = sel_begin; + setSelectionEnd(columns-1,lines-1+hist->getLines()-histCursor); + + writeSelectionToStream(stream,decoder); + + clearSelection(); +}*/ + +void Screen::writeToStream(TerminalCharacterDecoder* decoder, int from, int to) +{ + sel_begin = loc(0,from); + sel_TL = sel_begin; + sel_BR = loc(columns-1,to); + writeSelectionToStream(decoder); + clearSelection(); +} + +QString Screen::getHistoryLine(int no) +{ + sel_begin = loc(0,no); + sel_TL = sel_begin; + sel_BR = loc(columns-1,no); + return selectedText(false); +} + +void Screen::addHistLine() +{ + // add line to history buffer + // we have to take care about scrolling, too... + + if (hasScroll()) + { + int oldHistLines = hist->getLines(); + + hist->addCellsVector(screenLines[0]); + hist->addLine( lineProperties[0] & LINE_WRAPPED ); + + int newHistLines = hist->getLines(); + + bool beginIsTL = (sel_begin == sel_TL); + + // If the history is full, increment the count + // of dropped lines + if ( newHistLines == oldHistLines ) + _droppedLines++; + + // Adjust selection for the new point of reference + if (newHistLines > oldHistLines) + { + if (sel_begin != -1) + { + sel_TL += columns; + sel_BR += columns; + } + } + + if (sel_begin != -1) + { + // Scroll selection in history up + int top_BR = loc(0, 1+newHistLines); + + if (sel_TL < top_BR) + sel_TL -= columns; + + if (sel_BR < top_BR) + sel_BR -= columns; + + if (sel_BR < 0) + { + clearSelection(); + } + else + { + if (sel_TL < 0) + sel_TL = 0; + } + + if (beginIsTL) + sel_begin = sel_TL; + else + sel_begin = sel_BR; + } + } + +} + +int Screen::getHistLines() +{ + return hist->getLines(); +} + +void Screen::setScroll(const HistoryType& t , bool copyPreviousScroll) +{ + clearSelection(); + + if ( copyPreviousScroll ) + hist = t.scroll(hist); + else + { + HistoryScroll* oldScroll = hist; + hist = t.scroll(0); + delete oldScroll; + } +} + +bool Screen::hasScroll() +{ + return hist->hasScroll(); +} + +const HistoryType& Screen::getScroll() +{ + return hist->getType(); +} + +void Screen::setLineProperty(LineProperty property , bool enable) +{ + if ( enable ) + { + lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | property); + } + else + { + lineProperties[cuY] = (LineProperty)(lineProperties[cuY] & ~property); + } +} +void Screen::fillWithDefaultChar(Character* dest, int count) +{ + for (int i=0;i<count;i++) + dest[i] = defaultChar; +} |