/lib/Screen.cpp
C++ | 1569 lines | 986 code | 266 blank | 317 comment | 216 complexity | 6f11d5a097a697726fcb548b49de23f2 MD5 | raw file
- /*
- 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 );
- Q_UNUSED( size );
- 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(lastPos, lastPos);
- 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;
- }