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 }