File indexing completed on 2025-01-19 04:23:29

0001 /*
0002    This file is part of Konsole, an X terminal.
0003 
0004    Copyright 2007-2008 by Robert Knight <robert.knight@gmail.com>
0005    Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
0006 
0007    This program is free software; you can redistribute it and/or modify
0008    it under the terms of the GNU General Public License as published by
0009    the Free Software Foundation; either version 2 of the License, or
0010    (at your option) any later version.
0011 
0012    This program is distributed in the hope that it will be useful,
0013    but WITHOUT ANY WARRANTY; without even the implied warranty of
0014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015    GNU General Public License for more details.
0016 
0017    You should have received a copy of the GNU General Public License
0018    along with this program; if not, write to the Free Software
0019    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0020    02110-1301  USA.
0021    */
0022 
0023 // Own
0024 #include "Screen.h"
0025 
0026 // Standard
0027 #include <stdio.h>
0028 #include <stdlib.h>
0029 #include <unistd.h>
0030 #include <string.h>
0031 #include <ctype.h>
0032 
0033 // Qt
0034 #include <QTextStream>
0035 #include <QDate>
0036 
0037 // KDE
0038 //#include <kdebug.h>
0039 
0040 // Konsole
0041 #include "konsole_wcwidth.h"
0042 #include "TerminalCharacterDecoder.h"
0043 
0044 using namespace Konsole;
0045 
0046 //FIXME: this is emulation specific. Use false for xterm, true for ANSI.
0047 //FIXME: see if we can get this from terminfo.
0048 #define BS_CLEARS false
0049 
0050 //Macro to convert x,y position on screen to position within an image.
0051 //
0052 //Originally the image was stored as one large contiguous block of
0053 //memory, so a position within the image could be represented as an
0054 //offset from the beginning of the block.  For efficiency reasons this
0055 //is no longer the case.
0056 //Many internal parts of this class still use this representation for parameters and so on,
0057 //notably moveImage() and clearImage().
0058 //This macro converts from an X,Y position into an image offset.
0059 #ifndef loc
0060 #define loc(X,Y) ((Y)*columns+(X))
0061 #endif
0062 
0063 
0064 Character Screen::defaultChar = Character(' ',
0065         CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR),
0066         CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR),
0067         DEFAULT_RENDITION);
0068 
0069 //#define REVERSE_WRAPPED_LINES  // for wrapped line debug
0070 
0071     Screen::Screen(int l, int c)
0072 : lines(l),
0073     columns(c),
0074     screenLines(new ImageLine[lines+1] ),
0075     _scrolledLines(0),
0076     _droppedLines(0),
0077     history(new HistoryScrollNone()),
0078     cuX(0), cuY(0),
0079     currentRendition(0),
0080     _topMargin(0), _bottomMargin(0),
0081     selBegin(0), selTopLeft(0), selBottomRight(0),
0082     blockSelectionMode(false),
0083     effectiveForeground(CharacterColor()), effectiveBackground(CharacterColor()), effectiveRendition(0),
0084     lastPos(-1)
0085 {
0086     lineProperties.resize(lines+1);
0087     for (int i=0;i<lines+1;i++)
0088         lineProperties[i]=LINE_DEFAULT;
0089 
0090     initTabStops();
0091     clearSelection();
0092     reset();
0093 }
0094 
0095 /*! Destructor
0096 */
0097 
0098 Screen::~Screen()
0099 {
0100     delete[] screenLines;
0101     delete history;
0102 }
0103 
0104 void Screen::cursorUp(int n)
0105     //=CUU
0106 {
0107     if (n == 0) n = 1; // Default
0108     int stop = cuY < _topMargin ? 0 : _topMargin;
0109     cuX = qMin(columns-1,cuX); // nowrap!
0110     cuY = qMax(stop,cuY-n);
0111 }
0112 
0113 void Screen::cursorDown(int n)
0114     //=CUD
0115 {
0116     if (n == 0) n = 1; // Default
0117     int stop = cuY > _bottomMargin ? lines-1 : _bottomMargin;
0118     cuX = qMin(columns-1,cuX); // nowrap!
0119     cuY = qMin(stop,cuY+n);
0120 }
0121 
0122 void Screen::cursorLeft(int n)
0123     //=CUB
0124 {
0125     if (n == 0) n = 1; // Default
0126     cuX = qMin(columns-1,cuX); // nowrap!
0127     cuX = qMax(0,cuX-n);
0128 }
0129 
0130 void Screen::cursorRight(int n)
0131     //=CUF
0132 {
0133     if (n == 0) n = 1; // Default
0134     cuX = qMin(columns-1,cuX+n);
0135 }
0136 
0137 void Screen::setMargins(int top, int bot)
0138     //=STBM
0139 {
0140     if (top == 0) top = 1;      // Default
0141     if (bot == 0) bot = lines;  // Default
0142     top = top - 1;              // Adjust to internal lineno
0143     bot = bot - 1;              // Adjust to internal lineno
0144     if ( !( 0 <= top && top < bot && bot < lines ) )
0145     { //Debug()<<" setRegion("<<top<<","<<bot<<") : bad range.";
0146         return;                   // Default error action: ignore
0147     }
0148     _topMargin = top;
0149     _bottomMargin = bot;
0150     cuX = 0;
0151     cuY = getMode(MODE_Origin) ? top : 0;
0152 
0153 }
0154 
0155 int Screen::topMargin() const
0156 {
0157     return _topMargin;
0158 }
0159 int Screen::bottomMargin() const
0160 {
0161     return _bottomMargin;
0162 }
0163 
0164 void Screen::index()
0165     //=IND
0166 {
0167     if (cuY == _bottomMargin)
0168         scrollUp(1);
0169     else if (cuY < lines-1)
0170         cuY += 1;
0171 }
0172 
0173 void Screen::reverseIndex()
0174     //=RI
0175 {
0176     if (cuY == _topMargin)
0177         scrollDown(_topMargin,1);
0178     else if (cuY > 0)
0179         cuY -= 1;
0180 }
0181 
0182 void Screen::nextLine()
0183     //=NEL
0184 {
0185     toStartOfLine(); index();
0186 }
0187 
0188 void Screen::eraseChars(int n)
0189 {
0190     if (n == 0) n = 1; // Default
0191     int p = qMax(0,qMin(cuX+n-1,columns-1));
0192     clearImage(loc(cuX,cuY),loc(p,cuY),' ');
0193 }
0194 
0195 void Screen::deleteChars(int n)
0196 {
0197     Q_ASSERT( n >= 0 );
0198 
0199     // always delete at least one char
0200     if (n == 0)
0201         n = 1;
0202 
0203     // if cursor is beyond the end of the line there is nothing to do
0204     if ( cuX >= screenLines[cuY].count() )
0205         return;
0206 
0207     if ( cuX+n > screenLines[cuY].count() )
0208         n = screenLines[cuY].count() - cuX;
0209 
0210     Q_ASSERT( n >= 0 );
0211     Q_ASSERT( cuX+n <= screenLines[cuY].count() );
0212 
0213     screenLines[cuY].remove(cuX,n);
0214 }
0215 
0216 void Screen::insertChars(int n)
0217 {
0218     if (n == 0) n = 1; // Default
0219 
0220     if ( screenLines[cuY].size() < cuX )
0221         screenLines[cuY].resize(cuX);
0222 
0223     screenLines[cuY].insert(cuX,n,' ');
0224 
0225     if ( screenLines[cuY].count() > columns )
0226         screenLines[cuY].resize(columns);
0227 }
0228 
0229 void Screen::repeatChars(int count)
0230     //=REP
0231 {
0232     if (count == 0)
0233     {
0234         count = 1;
0235     }
0236     /**
0237      * From ECMA-48 version 5, section 8.3.103
0238      * If the character preceding REP is a control function or part of a
0239      * control function, the effect of REP is not defined by this Standard.
0240      *
0241      * So, a "normal" program should always use REP immediately after a visible
0242      * character (those other than escape sequences). So, lastDrawnChar can be
0243      * safely used.
0244      */
0245     for (int i = 0; i < count; i++)
0246     {
0247         displayCharacter(lastDrawnChar);
0248     }
0249 }
0250 
0251 void Screen::deleteLines(int n)
0252 {
0253     if (n == 0) n = 1; // Default
0254     scrollUp(cuY,n);
0255 }
0256 
0257 void Screen::insertLines(int n)
0258 {
0259     if (n == 0) n = 1; // Default
0260     scrollDown(cuY,n);
0261 }
0262 
0263 void Screen::setMode(int m)
0264 {
0265     currentModes[m] = true;
0266     switch(m)
0267     {
0268         case MODE_Origin : cuX = 0; cuY = _topMargin; break; //FIXME: home
0269     }
0270 }
0271 
0272 void Screen::resetMode(int m)
0273 {
0274     currentModes[m] = false;
0275     switch(m)
0276     {
0277         case MODE_Origin : cuX = 0; cuY = 0; break; //FIXME: home
0278     }
0279 }
0280 
0281 void Screen::saveMode(int m)
0282 {
0283     savedModes[m] = currentModes[m];
0284 }
0285 
0286 void Screen::restoreMode(int m)
0287 {
0288     currentModes[m] = savedModes[m];
0289 }
0290 
0291 bool Screen::getMode(int m) const
0292 {
0293     return currentModes[m];
0294 }
0295 
0296 void Screen::saveCursor()
0297 {
0298     savedState.cursorColumn = cuX;
0299     savedState.cursorLine  = cuY;
0300     savedState.rendition = currentRendition;
0301     savedState.foreground = currentForeground;
0302     savedState.background = currentBackground;
0303 }
0304 
0305 void Screen::restoreCursor()
0306 {
0307     cuX     = qMin(savedState.cursorColumn,columns-1);
0308     cuY     = qMin(savedState.cursorLine,lines-1);
0309     currentRendition   = savedState.rendition;
0310     currentForeground   = savedState.foreground;
0311     currentBackground   = savedState.background;
0312     updateEffectiveRendition();
0313 }
0314 
0315 void Screen::resizeImage(int new_lines, int new_columns)
0316 {
0317     if ((new_lines==lines) && (new_columns==columns)) return;
0318 
0319     if (cuY > new_lines-1)
0320     { // attempt to preserve focus and lines
0321         _bottomMargin = lines-1; //FIXME: margin lost
0322         for (int i = 0; i < cuY-(new_lines-1); i++)
0323         {
0324             addHistLine(); scrollUp(0,1);
0325         }
0326     }
0327 
0328     // create new screen lines and copy from old to new
0329 
0330     ImageLine* newScreenLines = new ImageLine[new_lines+1];
0331     for (int i=0; i < qMin(lines,new_lines+1) ;i++)
0332         newScreenLines[i]=screenLines[i];
0333     for (int i=lines;(i > 0) && (i<new_lines+1);i++)
0334         newScreenLines[i].resize( new_columns );
0335 
0336     lineProperties.resize(new_lines+1);
0337     for (int i=lines;(i > 0) && (i<new_lines+1);i++)
0338         lineProperties[i] = LINE_DEFAULT;
0339 
0340     clearSelection();
0341 
0342     delete[] screenLines;
0343     screenLines = newScreenLines;
0344 
0345     lines = new_lines;
0346     columns = new_columns;
0347     cuX = qMin(cuX,columns-1);
0348     cuY = qMin(cuY,lines-1);
0349 
0350     // FIXME: try to keep values, evtl.
0351     _topMargin=0;
0352     _bottomMargin=lines-1;
0353     initTabStops();
0354     clearSelection();
0355 }
0356 
0357 void Screen::setDefaultMargins()
0358 {
0359     _topMargin = 0;
0360     _bottomMargin = lines-1;
0361 }
0362 
0363 
0364 /*
0365    Clarifying rendition here and in the display.
0366 
0367    currently, the display's color table is
0368    0       1       2 .. 9    10 .. 17
0369    dft_fg, dft_bg, dim 0..7, intensive 0..7
0370 
0371    currentForeground, currentBackground contain values 0..8;
0372    - 0    = default color
0373    - 1..8 = ansi specified color
0374 
0375    re_fg, re_bg contain values 0..17
0376    due to the TerminalDisplay's color table
0377 
0378    rendition attributes are
0379 
0380    attr           widget screen
0381    -------------- ------ ------
0382    RE_UNDERLINE     XX     XX    affects foreground only
0383    RE_BLINK         XX     XX    affects foreground only
0384    RE_BOLD          XX     XX    affects foreground only
0385    RE_REVERSE       --     XX
0386    RE_TRANSPARENT   XX     --    affects background only
0387    RE_INTENSIVE     XX     --    affects foreground only
0388 
0389    Note that RE_BOLD is used in both widget
0390    and screen rendition. Since xterm/vt102
0391    is to poor to distinguish between bold
0392    (which is a font attribute) and intensive
0393    (which is a color attribute), we translate
0394    this and RE_BOLD in falls eventually appart
0395    into RE_BOLD and RE_INTENSIVE.
0396    */
0397 
0398 void Screen::reverseRendition(Character& p) const
0399 {
0400     CharacterColor f = p.foregroundColor;
0401     CharacterColor b = p.backgroundColor;
0402 
0403     p.foregroundColor = b;
0404     p.backgroundColor = f; //p->r &= ~RE_TRANSPARENT;
0405 }
0406 
0407 void Screen::updateEffectiveRendition()
0408 {
0409     effectiveRendition = currentRendition;
0410     if (currentRendition & RE_REVERSE)
0411     {
0412         effectiveForeground = currentBackground;
0413         effectiveBackground = currentForeground;
0414     }
0415     else
0416     {
0417         effectiveForeground = currentForeground;
0418         effectiveBackground = currentBackground;
0419     }
0420 
0421     if (currentRendition & RE_BOLD)
0422         effectiveForeground.setIntensive();
0423 }
0424 
0425 void Screen::copyFromHistory(Character* dest, int startLine, int count) const
0426 {
0427     Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= history->getLines() );
0428 
0429     for (int line = startLine; line < startLine + count; line++)
0430     {
0431         const int length = qMin(columns,history->getLineLen(line));
0432         const int destLineOffset  = (line-startLine)*columns;
0433 
0434         history->getCells(line,0,length,dest + destLineOffset);
0435 
0436         for (int column = length; column < columns; column++)
0437             dest[destLineOffset+column] = defaultChar;
0438 
0439         // invert selected text
0440         if (selBegin !=-1)
0441         {
0442             for (int column = 0; column < columns; column++)
0443             {
0444                 if (isSelected(column,line))
0445                 {
0446                     reverseRendition(dest[destLineOffset + column]);
0447                 }
0448             }
0449         }
0450     }
0451 }
0452 
0453 void Screen::copyFromScreen(Character* dest , int startLine , int count) const
0454 {
0455     Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= lines );
0456 
0457     for (int line = startLine; line < (startLine+count) ; line++)
0458     {
0459         int srcLineStartIndex  = line*columns;
0460         int destLineStartIndex = (line-startLine)*columns;
0461 
0462         for (int column = 0; column < columns; column++)
0463         {
0464             int srcIndex = srcLineStartIndex + column;
0465             int destIndex = destLineStartIndex + column;
0466 
0467             dest[destIndex] = screenLines[srcIndex/columns].value(srcIndex%columns,defaultChar);
0468 
0469             // invert selected text
0470             if (selBegin != -1 && isSelected(column,line + history->getLines()))
0471                 reverseRendition(dest[destIndex]);
0472         }
0473 
0474     }
0475 }
0476 
0477 void Screen::getImage( Character* dest, int size, int startLine, int endLine ) const
0478 {
0479     Q_ASSERT( startLine >= 0 );
0480     Q_ASSERT( endLine >= startLine && endLine < history->getLines() + lines );
0481 
0482     const int mergedLines = endLine - startLine + 1;
0483 
0484     Q_ASSERT( size >= mergedLines * columns );
0485     Q_UNUSED( size );
0486 
0487     const int linesInHistoryBuffer = qBound(0,history->getLines()-startLine,mergedLines);
0488     const int linesInScreenBuffer = mergedLines - linesInHistoryBuffer;
0489 
0490     // copy lines from history buffer
0491     if (linesInHistoryBuffer > 0)
0492         copyFromHistory(dest,startLine,linesInHistoryBuffer);
0493 
0494     // copy lines from screen buffer
0495     if (linesInScreenBuffer > 0)
0496         copyFromScreen(dest + linesInHistoryBuffer*columns,
0497                 startLine + linesInHistoryBuffer - history->getLines(),
0498                 linesInScreenBuffer);
0499 
0500     // invert display when in screen mode
0501     if (getMode(MODE_Screen))
0502     {
0503         for (int i = 0; i < mergedLines*columns; i++)
0504             reverseRendition(dest[i]); // for reverse display
0505     }
0506 
0507     // mark the character at the current cursor position
0508     int cursorIndex = loc(cuX, cuY + linesInHistoryBuffer);
0509     if(getMode(MODE_Cursor) && cursorIndex < columns*mergedLines)
0510         dest[cursorIndex].rendition |= RE_CURSOR;
0511 }
0512 
0513 QVector<LineProperty> Screen::getLineProperties( int startLine , int endLine ) const
0514 {
0515     Q_ASSERT( startLine >= 0 );
0516     Q_ASSERT( endLine >= startLine && endLine < history->getLines() + lines );
0517 
0518     const int mergedLines = endLine-startLine+1;
0519     const int linesInHistory = qBound(0,history->getLines()-startLine,mergedLines);
0520     const int linesInScreen = mergedLines - linesInHistory;
0521 
0522     QVector<LineProperty> result(mergedLines);
0523     int index = 0;
0524 
0525     // copy properties for lines in history
0526     for (int line = startLine; line < startLine + linesInHistory; line++)
0527     {
0528         //TODO Support for line properties other than wrapped lines
0529         if (history->isWrappedLine(line))
0530         {
0531             result[index] = (LineProperty)(result[index] | LINE_WRAPPED);
0532         }
0533         index++;
0534     }
0535 
0536     // copy properties for lines in screen buffer
0537     const int firstScreenLine = startLine + linesInHistory - history->getLines();
0538     for (int line = firstScreenLine; line < firstScreenLine+linesInScreen; line++)
0539     {
0540         result[index]=lineProperties[line];
0541         index++;
0542     }
0543 
0544     return result;
0545 }
0546 
0547 void Screen::reset(bool clearScreen)
0548 {
0549     setMode(MODE_Wrap  ); saveMode(MODE_Wrap  );  // wrap at end of margin
0550     resetMode(MODE_Origin); saveMode(MODE_Origin);  // position refere to [1,1]
0551     resetMode(MODE_Insert); saveMode(MODE_Insert);  // overstroke
0552     setMode(MODE_Cursor);                         // cursor visible
0553     resetMode(MODE_Screen);                         // screen not inverse
0554     resetMode(MODE_NewLine);
0555 
0556     _topMargin=0;
0557     _bottomMargin=lines-1;
0558 
0559     setDefaultRendition();
0560     saveCursor();
0561 
0562     if ( clearScreen )
0563         clear();
0564 }
0565 
0566 void Screen::clear()
0567 {
0568     clearEntireScreen();
0569     home();
0570 }
0571 
0572 void Screen::backspace()
0573 {
0574     cuX = qMin(columns-1,cuX); // nowrap!
0575     cuX = qMax(0,cuX-1);
0576 
0577     if (screenLines[cuY].size() < cuX+1)
0578         screenLines[cuY].resize(cuX+1);
0579 
0580     if (BS_CLEARS)
0581         screenLines[cuY][cuX].character = ' ';
0582 }
0583 
0584 void Screen::tab(int n)
0585 {
0586     // note that TAB is a format effector (does not write ' ');
0587     if (n == 0) n = 1;
0588     while((n > 0) && (cuX < columns-1))
0589     {
0590         cursorRight(1);
0591         while((cuX < columns-1) && !tabStops[cuX])
0592             cursorRight(1);
0593         n--;
0594     }
0595 }
0596 
0597 void Screen::backtab(int n)
0598 {
0599     // note that TAB is a format effector (does not write ' ');
0600     if (n == 0) n = 1;
0601     while((n > 0) && (cuX > 0))
0602     {
0603         cursorLeft(1); while((cuX > 0) && !tabStops[cuX]) cursorLeft(1);
0604         n--;
0605     }
0606 }
0607 
0608 void Screen::clearTabStops()
0609 {
0610     for (int i = 0; i < columns; i++) tabStops[i] = false;
0611 }
0612 
0613 void Screen::changeTabStop(bool set)
0614 {
0615     if (cuX >= columns) return;
0616     tabStops[cuX] = set;
0617 }
0618 
0619 void Screen::initTabStops()
0620 {
0621     tabStops.resize(columns);
0622 
0623     // Arrg! The 1st tabstop has to be one longer than the other.
0624     // i.e. the kids start counting from 0 instead of 1.
0625     // Other programs might behave correctly. Be aware.
0626     for (int i = 0; i < columns; i++)
0627         tabStops[i] = (i%8 == 0 && i != 0);
0628 }
0629 
0630 void Screen::newLine()
0631 {
0632     if (getMode(MODE_NewLine))
0633         toStartOfLine();
0634     index();
0635 }
0636 
0637 void Screen::checkSelection(int from, int to)
0638 {
0639     if (selBegin == -1)
0640         return;
0641     int scr_TL = loc(0, history->getLines());
0642     //Clear entire selection if it overlaps region [from, to]
0643     if ( (selBottomRight >= (from+scr_TL)) && (selTopLeft <= (to+scr_TL)) )
0644         clearSelection();
0645 }
0646 
0647 void Screen::displayCharacter(wchar_t c)
0648 {
0649     // Note that VT100 does wrapping BEFORE putting the character.
0650     // This has impact on the assumption of valid cursor positions.
0651     // We indicate the fact that a newline has to be triggered by
0652     // putting the cursor one right to the last column of the screen.
0653 
0654     int w = konsole_wcwidth(c);
0655     if (w <= 0)
0656         return;
0657 
0658     if (cuX+w > columns) {
0659         if (getMode(MODE_Wrap)) {
0660             lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | LINE_WRAPPED);
0661             nextLine();
0662         }
0663         else
0664             cuX = columns-w;
0665     }
0666 
0667     // ensure current line vector has enough elements
0668     int size = screenLines[cuY].size();
0669     if (size < cuX+w)
0670     {
0671         screenLines[cuY].resize(cuX+w);
0672     }
0673 
0674     if (getMode(MODE_Insert)) insertChars(w);
0675 
0676     lastPos = loc(cuX,cuY);
0677 
0678     // check if selection is still valid.
0679     checkSelection(lastPos, lastPos);
0680 
0681     Character& currentChar = screenLines[cuY][cuX];
0682 
0683     currentChar.character = c;
0684     currentChar.foregroundColor = effectiveForeground;
0685     currentChar.backgroundColor = effectiveBackground;
0686     currentChar.rendition = effectiveRendition;
0687 
0688     lastDrawnChar = c;
0689 
0690     int i = 0;
0691     int newCursorX = cuX + w--;
0692     while(w)
0693     {
0694         i++;
0695 
0696         if ( screenLines[cuY].size() < cuX + i + 1 )
0697             screenLines[cuY].resize(cuX+i+1);
0698 
0699         Character& ch = screenLines[cuY][cuX + i];
0700         ch.character = 0;
0701         ch.foregroundColor = effectiveForeground;
0702         ch.backgroundColor = effectiveBackground;
0703         ch.rendition = effectiveRendition;
0704 
0705         w--;
0706     }
0707     cuX = newCursorX;
0708 }
0709 
0710 void Screen::compose(const QString& /*compose*/)
0711 {
0712     Q_ASSERT( 0 /*Not implemented yet*/ );
0713 
0714     /*  if (lastPos == -1)
0715         return;
0716 
0717         QChar c(image[lastPos].character);
0718         compose.prepend(c);
0719     //compose.compose(); ### FIXME!
0720     image[lastPos].character = compose[0].unicode();*/
0721 }
0722 
0723 int Screen::scrolledLines() const
0724 {
0725     return _scrolledLines;
0726 }
0727 int Screen::droppedLines() const
0728 {
0729     return _droppedLines;
0730 }
0731 void Screen::resetDroppedLines()
0732 {
0733     _droppedLines = 0;
0734 }
0735 void Screen::resetScrolledLines()
0736 {
0737     _scrolledLines = 0;
0738 }
0739 
0740 void Screen::scrollUp(int n)
0741 {
0742     if (n == 0) n = 1; // Default
0743     if (_topMargin == 0) addHistLine(); // history.history
0744     scrollUp(_topMargin, n);
0745 }
0746 
0747 QRect Screen::lastScrolledRegion() const
0748 {
0749     return _lastScrolledRegion;
0750 }
0751 
0752 void Screen::scrollUp(int from, int n)
0753 {
0754     if (n <= 0)
0755         return;
0756     if (from > _bottomMargin)
0757         return;
0758     if (from + n > _bottomMargin)
0759         n = _bottomMargin + 1 - from;
0760 
0761     _scrolledLines -= n;
0762     _lastScrolledRegion = QRect(0,_topMargin,columns-1,(_bottomMargin-_topMargin));
0763 
0764     //FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds.
0765     moveImage(loc(0,from),loc(0,from+n),loc(columns,_bottomMargin));
0766     clearImage(loc(0,_bottomMargin-n+1),loc(columns-1,_bottomMargin),' ');
0767 }
0768 
0769 void Screen::scrollDown(int n)
0770 {
0771     if (n == 0) n = 1; // Default
0772     scrollDown(_topMargin, n);
0773 }
0774 
0775 void Screen::scrollDown(int from, int n)
0776 {
0777     _scrolledLines += n;
0778 
0779     //FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds.
0780     if (n <= 0)
0781         return;
0782     if (from > _bottomMargin)
0783         return;
0784     if (from + n > _bottomMargin)
0785         n = _bottomMargin - from;
0786     moveImage(loc(0,from+n),loc(0,from),loc(columns-1,_bottomMargin-n));
0787     clearImage(loc(0,from),loc(columns-1,from+n-1),' ');
0788 }
0789 
0790 void Screen::setCursorYX(int y, int x)
0791 {
0792     setCursorY(y); setCursorX(x);
0793 }
0794 
0795 void Screen::setCursorX(int x)
0796 {
0797     if (x == 0) x = 1; // Default
0798     x -= 1; // Adjust
0799     cuX = qMax(0,qMin(columns-1, x));
0800 }
0801 
0802 void Screen::setCursorY(int y)
0803 {
0804     if (y == 0) y = 1; // Default
0805     y -= 1; // Adjust
0806     cuY = qMax(0,qMin(lines  -1, y + (getMode(MODE_Origin) ? _topMargin : 0) ));
0807 }
0808 
0809 void Screen::home()
0810 {
0811     cuX = 0;
0812     cuY = 0;
0813 }
0814 
0815 void Screen::toStartOfLine()
0816 {
0817     cuX = 0;
0818 }
0819 
0820 int Screen::getCursorX() const
0821 {
0822     return cuX;
0823 }
0824 
0825 int Screen::getCursorY() const
0826 {
0827     return cuY;
0828 }
0829 
0830 void Screen::clearImage(int loca, int loce, char c)
0831 {
0832     int scr_TL=loc(0,history->getLines());
0833     //FIXME: check positions
0834 
0835     //Clear entire selection if it overlaps region to be moved...
0836     if ( (selBottomRight > (loca+scr_TL) )&&(selTopLeft < (loce+scr_TL)) )
0837     {
0838         clearSelection();
0839     }
0840 
0841     int topLine = loca/columns;
0842     int bottomLine = loce/columns;
0843 
0844     Character clearCh(c,currentForeground,currentBackground,DEFAULT_RENDITION);
0845 
0846     //if the character being used to clear the area is the same as the
0847     //default character, the affected lines can simply be shrunk.
0848     bool isDefaultCh = (clearCh == Character());
0849 
0850     for (int y=topLine;y<=bottomLine;y++)
0851     {
0852         lineProperties[y] = 0;
0853 
0854         int endCol = ( y == bottomLine) ? loce%columns : columns-1;
0855         int startCol = ( y == topLine ) ? loca%columns : 0;
0856 
0857         QVector<Character>& line = screenLines[y];
0858 
0859         if ( isDefaultCh && endCol == columns-1 )
0860         {
0861             line.resize(startCol);
0862         }
0863         else
0864         {
0865             if (line.size() < endCol + 1)
0866                 line.resize(endCol+1);
0867 
0868             Character* data = line.data();
0869             for (int i=startCol;i<=endCol;i++)
0870                 data[i]=clearCh;
0871         }
0872     }
0873 }
0874 
0875 void Screen::moveImage(int dest, int sourceBegin, int sourceEnd)
0876 {
0877     Q_ASSERT( sourceBegin <= sourceEnd );
0878 
0879     int lines=(sourceEnd-sourceBegin)/columns;
0880 
0881     //move screen image and line properties:
0882     //the source and destination areas of the image may overlap,
0883     //so it matters that we do the copy in the right order -
0884     //forwards if dest < sourceBegin or backwards otherwise.
0885     //(search the web for 'memmove implementation' for details)
0886     if (dest < sourceBegin)
0887     {
0888         for (int i=0;i<=lines;i++)
0889         {
0890             screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ];
0891             lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i];
0892         }
0893     }
0894     else
0895     {
0896         for (int i=lines;i>=0;i--)
0897         {
0898             screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ];
0899             lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i];
0900         }
0901     }
0902 
0903     if (lastPos != -1)
0904     {
0905         int diff = dest - sourceBegin; // Scroll by this amount
0906         lastPos += diff;
0907         if ((lastPos < 0) || (lastPos >= (lines*columns)))
0908             lastPos = -1;
0909     }
0910 
0911     // Adjust selection to follow scroll.
0912     if (selBegin != -1)
0913     {
0914         bool beginIsTL = (selBegin == selTopLeft);
0915         int diff = dest - sourceBegin; // Scroll by this amount
0916         int scr_TL=loc(0,history->getLines());
0917         int srca = sourceBegin+scr_TL; // Translate index from screen to global
0918         int srce = sourceEnd+scr_TL; // Translate index from screen to global
0919         int desta = srca+diff;
0920         int deste = srce+diff;
0921 
0922         if ((selTopLeft >= srca) && (selTopLeft <= srce))
0923             selTopLeft += diff;
0924         else if ((selTopLeft >= desta) && (selTopLeft <= deste))
0925             selBottomRight = -1; // Clear selection (see below)
0926 
0927         if ((selBottomRight >= srca) && (selBottomRight <= srce))
0928             selBottomRight += diff;
0929         else if ((selBottomRight >= desta) && (selBottomRight <= deste))
0930             selBottomRight = -1; // Clear selection (see below)
0931 
0932         if (selBottomRight < 0)
0933         {
0934             clearSelection();
0935         }
0936         else
0937         {
0938             if (selTopLeft < 0)
0939                 selTopLeft = 0;
0940         }
0941 
0942         if (beginIsTL)
0943             selBegin = selTopLeft;
0944         else
0945             selBegin = selBottomRight;
0946     }
0947 }
0948 
0949 void Screen::clearToEndOfScreen()
0950 {
0951     clearImage(loc(cuX,cuY),loc(columns-1,lines-1),' ');
0952 }
0953 
0954 void Screen::clearToBeginOfScreen()
0955 {
0956     clearImage(loc(0,0),loc(cuX,cuY),' ');
0957 }
0958 
0959 void Screen::clearEntireScreen()
0960 {
0961     // Add entire screen to history
0962     for (int i = 0; i < (lines-1); i++)
0963     {
0964         addHistLine(); scrollUp(0,1);
0965     }
0966 
0967     clearImage(loc(0,0),loc(columns-1,lines-1),' ');
0968 }
0969 
0970 /*! fill screen with 'E'
0971   This is to aid screen alignment
0972   */
0973 
0974 void Screen::helpAlign()
0975 {
0976     clearImage(loc(0,0),loc(columns-1,lines-1),'E');
0977 }
0978 
0979 void Screen::clearToEndOfLine()
0980 {
0981     clearImage(loc(cuX,cuY),loc(columns-1,cuY),' ');
0982 }
0983 
0984 void Screen::clearToBeginOfLine()
0985 {
0986     clearImage(loc(0,cuY),loc(cuX,cuY),' ');
0987 }
0988 
0989 void Screen::clearEntireLine()
0990 {
0991     clearImage(loc(0,cuY),loc(columns-1,cuY),' ');
0992 }
0993 
0994 void Screen::setRendition(int re)
0995 {
0996     currentRendition |= re;
0997     updateEffectiveRendition();
0998 }
0999 
1000 void Screen::resetRendition(int re)
1001 {
1002     currentRendition &= ~re;
1003     updateEffectiveRendition();
1004 }
1005 
1006 void Screen::setDefaultRendition()
1007 {
1008     setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR);
1009     setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR);
1010     currentRendition   = DEFAULT_RENDITION;
1011     updateEffectiveRendition();
1012 }
1013 
1014 void Screen::setForeColor(int space, int color)
1015 {
1016     currentForeground = CharacterColor(space, color);
1017 
1018     if ( currentForeground.isValid() )
1019         updateEffectiveRendition();
1020     else
1021         setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR);
1022 }
1023 
1024 void Screen::setBackColor(int space, int color)
1025 {
1026     currentBackground = CharacterColor(space, color);
1027 
1028     if ( currentBackground.isValid() )
1029         updateEffectiveRendition();
1030     else
1031         setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR);
1032 }
1033 
1034 void Screen::clearSelection()
1035 {
1036     selBottomRight = -1;
1037     selTopLeft = -1;
1038     selBegin = -1;
1039 }
1040 
1041 void Screen::getSelectionStart(int& column , int& line) const
1042 {
1043     if ( selTopLeft != -1 )
1044     {
1045         column = selTopLeft % columns;
1046         line = selTopLeft / columns;
1047     }
1048     else
1049     {
1050         column = cuX + getHistLines();
1051         line = cuY + getHistLines();
1052     }
1053 }
1054 void Screen::getSelectionEnd(int& column , int& line) const
1055 {
1056     if ( selBottomRight != -1 )
1057     {
1058         column = selBottomRight % columns;
1059         line = selBottomRight / columns;
1060     }
1061     else
1062     {
1063         column = cuX + getHistLines();
1064         line = cuY + getHistLines();
1065     }
1066 }
1067 void Screen::setSelectionStart(const int x, const int y, const bool mode)
1068 {
1069     selBegin = loc(x,y);
1070     /* FIXME, HACK to correct for x too far to the right... */
1071     if (x == columns) selBegin--;
1072 
1073     selBottomRight = selBegin;
1074     selTopLeft = selBegin;
1075     blockSelectionMode = mode;
1076 }
1077 
1078 void Screen::setSelectionEnd( const int x, const int y)
1079 {
1080     if (selBegin == -1)
1081         return;
1082 
1083     int endPos =  loc(x,y);
1084 
1085     if (endPos < selBegin)
1086     {
1087         selTopLeft = endPos;
1088         selBottomRight = selBegin;
1089     }
1090     else
1091     {
1092         /* FIXME, HACK to correct for x too far to the right... */
1093         if (x == columns)
1094             endPos--;
1095 
1096         selTopLeft = selBegin;
1097         selBottomRight = endPos;
1098     }
1099 
1100     // Normalize the selection in column mode
1101     if (blockSelectionMode)
1102     {
1103         int topRow = selTopLeft / columns;
1104         int topColumn = selTopLeft % columns;
1105         int bottomRow = selBottomRight / columns;
1106         int bottomColumn = selBottomRight % columns;
1107 
1108         selTopLeft = loc(qMin(topColumn,bottomColumn),topRow);
1109         selBottomRight = loc(qMax(topColumn,bottomColumn),bottomRow);
1110     }
1111 }
1112 
1113 void Screen::selectAll()
1114 {
1115     selBegin = 0;
1116     selTopLeft = 0;
1117     int endPos = (getHistLines() + getCursorY() + 1) * columns - 1;
1118     selBottomRight = endPos;
1119 }
1120 
1121 bool Screen::isSelected( const int x,const int y) const
1122 {
1123     bool columnInSelection = true;
1124     if (blockSelectionMode)
1125     {
1126         columnInSelection = x >= (selTopLeft % columns) &&
1127             x <= (selBottomRight % columns);
1128     }
1129 
1130     int pos = loc(x,y);
1131     return pos >= selTopLeft && pos <= selBottomRight && columnInSelection;
1132 }
1133 
1134 QString Screen::selectedText(bool preserveLineBreaks) const
1135 {
1136     QString result;
1137     QTextStream stream(&result, QIODevice::ReadWrite);
1138 
1139     PlainTextDecoder decoder;
1140     decoder.begin(&stream);
1141     writeSelectionToStream(&decoder , preserveLineBreaks);
1142     decoder.end();
1143 
1144     return result;
1145 }
1146 
1147 bool Screen::isSelectionValid() const
1148 {
1149     return selTopLeft >= 0 && selBottomRight >= 0;
1150 }
1151 
1152 void Screen::writeSelectionToStream(TerminalCharacterDecoder* decoder ,
1153         bool preserveLineBreaks) const
1154 {
1155     if (!isSelectionValid())
1156         return;
1157     writeToStream(decoder,selTopLeft,selBottomRight,preserveLineBreaks);
1158 }
1159 
1160 void Screen::writeToStream(TerminalCharacterDecoder* decoder,
1161         int startIndex, int endIndex,
1162         bool preserveLineBreaks) const
1163 {
1164     int top = startIndex / columns;
1165     int left = startIndex % columns;
1166 
1167     int bottom = endIndex / columns;
1168     int right = endIndex % columns;
1169 
1170     Q_ASSERT( top >= 0 && left >= 0 && bottom >= 0 && right >= 0 );
1171 
1172     for (int y=top;y<=bottom;y++)
1173     {
1174         int start = 0;
1175         if ( y == top || blockSelectionMode ) start = left;
1176 
1177         int count = -1;
1178         if ( y == bottom || blockSelectionMode ) count = right - start + 1;
1179 
1180         const bool appendNewLine = ( y != bottom );
1181         int copied = copyLineToStream( y,
1182                 start,
1183                 count,
1184                 decoder,
1185                 appendNewLine,
1186                 preserveLineBreaks );
1187 
1188         // if the selection goes beyond the end of the last line then
1189         // append a new line character.
1190         //
1191         // this makes it possible to 'select' a trailing new line character after
1192         // the text on a line.
1193         if ( y == bottom &&
1194                 copied < count    )
1195         {
1196             Character newLineChar('\n');
1197             decoder->decodeLine(&newLineChar,1,0);
1198         }
1199     }
1200 }
1201 
1202 int Screen::copyLineToStream(int line ,
1203         int start,
1204         int count,
1205         TerminalCharacterDecoder* decoder,
1206         bool appendNewLine,
1207         bool preserveLineBreaks) const
1208 {
1209     //buffer to hold characters for decoding
1210     //the buffer is static to avoid initialising every
1211     //element on each call to copyLineToStream
1212     //(which is unnecessary since all elements will be overwritten anyway)
1213     static const int MAX_CHARS = 1024;
1214     static Character characterBuffer[MAX_CHARS];
1215 
1216     Q_ASSERT( count < MAX_CHARS );
1217 
1218     LineProperty currentLineProperties = 0;
1219 
1220     //determine if the line is in the history buffer or the screen image
1221     if (line < history->getLines())
1222     {
1223         const int lineLength = history->getLineLen(line);
1224 
1225         // ensure that start position is before end of line
1226         start = qMin(start,qMax(0,lineLength-1));
1227 
1228         // retrieve line from history buffer.  It is assumed
1229         // that the history buffer does not store trailing white space
1230         // at the end of the line, so it does not need to be trimmed here
1231         if (count == -1)
1232         {
1233             count = lineLength-start;
1234         }
1235         else
1236         {
1237             count = qMin(start+count,lineLength)-start;
1238         }
1239 
1240         // safety checks
1241         Q_ASSERT( start >= 0 );
1242         Q_ASSERT( count >= 0 );
1243         Q_ASSERT( (start+count) <= history->getLineLen(line) );
1244 
1245         history->getCells(line,start,count,characterBuffer);
1246 
1247         if ( history->isWrappedLine(line) )
1248             currentLineProperties |= LINE_WRAPPED;
1249     }
1250     else
1251     {
1252         if ( count == -1 )
1253             count = columns - start;
1254 
1255         Q_ASSERT( count >= 0 );
1256 
1257         const int screenLine = line-history->getLines();
1258 
1259         Character* data = screenLines[screenLine].data();
1260         int length = screenLines[screenLine].count();
1261 
1262         //retrieve line from screen image
1263         for (int i=start;i < qMin(start+count,length);i++)
1264         {
1265             characterBuffer[i-start] = data[i];
1266         }
1267 
1268         // count cannot be any greater than length
1269         count = qBound(0,count,length-start);
1270 
1271         Q_ASSERT( screenLine < lineProperties.count() );
1272         currentLineProperties |= lineProperties[screenLine];
1273     }
1274 
1275     // add new line character at end
1276     const bool omitLineBreak = (currentLineProperties & LINE_WRAPPED) ||
1277         !preserveLineBreaks;
1278 
1279     if ( !omitLineBreak && appendNewLine && (count+1 < MAX_CHARS) )
1280     {
1281         characterBuffer[count] = '\n';
1282         count++;
1283     }
1284 
1285     //decode line and write to text stream
1286     decoder->decodeLine( (Character*) characterBuffer ,
1287             count, currentLineProperties );
1288 
1289     return count;
1290 }
1291 
1292 void Screen::writeLinesToStream(TerminalCharacterDecoder* decoder, int fromLine, int toLine) const
1293 {
1294     writeToStream(decoder,loc(0,fromLine),loc(columns-1,toLine));
1295 }
1296 
1297 void Screen::addHistLine()
1298 {
1299     // add line to history buffer
1300     // we have to take care about scrolling, too...
1301 
1302     if (hasScroll())
1303     {
1304         int oldHistLines = history->getLines();
1305 
1306         history->addCellsVector(screenLines[0]);
1307         history->addLine( lineProperties[0] & LINE_WRAPPED );
1308 
1309         int newHistLines = history->getLines();
1310 
1311         bool beginIsTL = (selBegin == selTopLeft);
1312 
1313         // If the history is full, increment the count
1314         // of dropped lines
1315         if ( newHistLines == oldHistLines )
1316             _droppedLines++;
1317 
1318         // Adjust selection for the new point of reference
1319         if (newHistLines > oldHistLines)
1320         {
1321             if (selBegin != -1)
1322             {
1323                 selTopLeft += columns;
1324                 selBottomRight += columns;
1325             }
1326         }
1327 
1328         if (selBegin != -1)
1329         {
1330             // Scroll selection in history up
1331             int top_BR = loc(0, 1+newHistLines);
1332 
1333             if (selTopLeft < top_BR)
1334                 selTopLeft -= columns;
1335 
1336             if (selBottomRight < top_BR)
1337                 selBottomRight -= columns;
1338 
1339             if (selBottomRight < 0)
1340                 clearSelection();
1341             else
1342             {
1343                 if (selTopLeft < 0)
1344                     selTopLeft = 0;
1345             }
1346 
1347             if (beginIsTL)
1348                 selBegin = selTopLeft;
1349             else
1350                 selBegin = selBottomRight;
1351         }
1352     }
1353 
1354 }
1355 
1356 int Screen::getHistLines() const
1357 {
1358     return history->getLines();
1359 }
1360 
1361 void Screen::setScroll(const HistoryType& t , bool copyPreviousScroll)
1362 {
1363     clearSelection();
1364 
1365     if ( copyPreviousScroll )
1366         history = t.scroll(history);
1367     else
1368     {
1369         HistoryScroll* oldScroll = history;
1370         history = t.scroll(0);
1371         delete oldScroll;
1372     }
1373 }
1374 
1375 bool Screen::hasScroll() const
1376 {
1377     return history->hasScroll();
1378 }
1379 
1380 const HistoryType& Screen::getScroll() const
1381 {
1382     return history->getType();
1383 }
1384 
1385 void Screen::setLineProperty(LineProperty property , bool enable)
1386 {
1387     if ( enable )
1388         lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | property);
1389     else
1390         lineProperties[cuY] = (LineProperty)(lineProperties[cuY] & ~property);
1391 }
1392 void Screen::fillWithDefaultChar(Character* dest, int count)
1393 {
1394     for (int i=0;i<count;i++)
1395         dest[i] = defaultChar;
1396 }