• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Konsole

Screen.cpp

Go to the documentation of this file.
00001 /*
00002    This file is part of Konsole, an X terminal.
00003 
00004    Copyright 2007-2008 by Robert Knight <robert.knight@gmail.com>
00005    Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2 of the License, or
00010    (at your option) any later version.
00011 
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016 
00017    You should have received a copy of the GNU General Public License
00018    along with this program; if not, write to the Free Software
00019    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00020    02110-1301  USA.
00021    */
00022 
00023 // Own
00024 #include "Screen.h"
00025 
00026 // Standard
00027 #include <stdio.h>
00028 #include <stdlib.h>
00029 #include <unistd.h>
00030 #include <assert.h>
00031 #include <string.h>
00032 #include <ctype.h>
00033 
00034 // Qt
00035 #include <QtCore/QTextStream>
00036 #include <QtCore/QDate>
00037 
00038 // KDE
00039 #include <kdebug.h>
00040 
00041 // Konsole
00042 #include "konsole_wcwidth.h"
00043 #include "TerminalCharacterDecoder.h"
00044 
00045 using namespace Konsole;
00046 
00047 //FIXME: this is emulation specific. Use false for xterm, true for ANSI.
00048 //FIXME: see if we can get this from terminfo.
00049 #define BS_CLEARS false
00050 
00051 //Macro to convert x,y position on screen to position within an image.
00052 //
00053 //Originally the image was stored as one large contiguous block of 
00054 //memory, so a position within the image could be represented as an
00055 //offset from the beginning of the block.  For efficiency reasons this
00056 //is no longer the case.  
00057 //Many internal parts of this class still use this representation for parameters and so on,
00058 //notably moveImage() and clearImage().
00059 //This macro converts from an X,Y position into an image offset.
00060 #ifndef loc
00061 #define loc(X,Y) ((Y)*columns+(X))
00062 #endif
00063 
00064 
00065 Character Screen::defaultChar = Character(' ',
00066         CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR),
00067         CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR),
00068         DEFAULT_RENDITION);
00069 
00070 //#define REVERSE_WRAPPED_LINES  // for wrapped line debug
00071 
00072     Screen::Screen(int l, int c)
00073 : lines(l),
00074     columns(c),
00075     screenLines(new ImageLine[lines+1] ),
00076     _scrolledLines(0),
00077     _droppedLines(0),
00078     history(new HistoryScrollNone()),
00079     cuX(0), cuY(0),
00080     currentRendition(0),
00081     _topMargin(0), _bottomMargin(0),
00082     selBegin(0), selTopLeft(0), selBottomRight(0),
00083     blockSelectionMode(false),
00084     effectiveForeground(CharacterColor()), effectiveBackground(CharacterColor()), effectiveRendition(0),
00085     lastPos(-1)
00086 {
00087     lineProperties.resize(lines+1);
00088     for (int i=0;i<lines+1;i++)
00089         lineProperties[i]=LINE_DEFAULT;
00090 
00091     initTabStops();
00092     clearSelection();
00093     reset();
00094 }
00095 
00099 Screen::~Screen()
00100 {
00101     delete[] screenLines;
00102     delete history;
00103 }
00104 
00105 void Screen::cursorUp(int n)
00106     //=CUU
00107 {
00108     if (n == 0) n = 1; // Default
00109     int stop = cuY < _topMargin ? 0 : _topMargin;
00110     cuX = qMin(columns-1,cuX); // nowrap!
00111     cuY = qMax(stop,cuY-n);
00112 }
00113 
00114 void Screen::cursorDown(int n)
00115     //=CUD
00116 {
00117     if (n == 0) n = 1; // Default
00118     int stop = cuY > _bottomMargin ? lines-1 : _bottomMargin;
00119     cuX = qMin(columns-1,cuX); // nowrap!
00120     cuY = qMin(stop,cuY+n);
00121 }
00122 
00123 void Screen::cursorLeft(int n)
00124     //=CUB
00125 {
00126     if (n == 0) n = 1; // Default
00127     cuX = qMin(columns-1,cuX); // nowrap!
00128     cuX = qMax(0,cuX-n);
00129 }
00130 
00131 void Screen::cursorRight(int n)
00132     //=CUF
00133 {
00134     if (n == 0) n = 1; // Default
00135     cuX = qMin(columns-1,cuX+n);
00136 }
00137 
00138 void Screen::setMargins(int top, int bot)
00139     //=STBM
00140 {
00141     if (top == 0) top = 1;      // Default
00142     if (bot == 0) bot = lines;  // Default
00143     top = top - 1;              // Adjust to internal lineno
00144     bot = bot - 1;              // Adjust to internal lineno
00145     if ( !( 0 <= top && top < bot && bot < lines ) )
00146     { kDebug()<<" setRegion("<<top<<","<<bot<<") : bad range.";
00147         return;                   // Default error action: ignore
00148     }
00149     _topMargin = top;
00150     _bottomMargin = bot;
00151     cuX = 0;
00152     cuY = getMode(MODE_Origin) ? top : 0;
00153 
00154 }
00155 
00156 int Screen::topMargin() const
00157 {
00158     return _topMargin;
00159 }
00160 int Screen::bottomMargin() const
00161 {
00162     return _bottomMargin;
00163 }
00164 
00165 void Screen::index()
00166     //=IND
00167 {
00168     if (cuY == _bottomMargin)
00169         scrollUp(1);
00170     else if (cuY < lines-1)
00171         cuY += 1;
00172 }
00173 
00174 void Screen::reverseIndex()
00175     //=RI
00176 {
00177     if (cuY == _topMargin)
00178         scrollDown(_topMargin,1);
00179     else if (cuY > 0)
00180         cuY -= 1;
00181 }
00182 
00183 void Screen::nextLine()
00184     //=NEL
00185 {
00186     toStartOfLine(); index();
00187 }
00188 
00189 void Screen::eraseChars(int n)
00190 {
00191     if (n == 0) n = 1; // Default
00192     int p = qMax(0,qMin(cuX+n-1,columns-1));
00193     clearImage(loc(cuX,cuY),loc(p,cuY),' ');
00194 }
00195 
00196 void Screen::deleteChars(int n)
00197 {
00198     Q_ASSERT( n >= 0 );
00199 
00200     // always delete at least one char
00201     if (n == 0) 
00202         n = 1; 
00203 
00204     // if cursor is beyond the end of the line there is nothing to do
00205     if ( cuX >= screenLines[cuY].count() )
00206         return;
00207 
00208     if ( cuX+n >= screenLines[cuY].count() ) 
00209         n = screenLines[cuY].count() - 1 - cuX;
00210 
00211     Q_ASSERT( n >= 0 );
00212     Q_ASSERT( cuX+n < screenLines[cuY].count() );
00213 
00214     screenLines[cuY].remove(cuX,n);
00215 }
00216 
00217 void Screen::insertChars(int n)
00218 {
00219     if (n == 0) n = 1; // Default
00220 
00221     if ( screenLines[cuY].size() < cuX )
00222         screenLines[cuY].resize(cuX);
00223 
00224     screenLines[cuY].insert(cuX,n,' ');
00225 
00226     if ( screenLines[cuY].count() > columns )
00227         screenLines[cuY].resize(columns);
00228 }
00229 
00230 void Screen::deleteLines(int n)
00231 {
00232     if (n == 0) n = 1; // Default
00233     scrollUp(cuY,n);
00234 }
00235 
00236 void Screen::insertLines(int n)
00237 {
00238     if (n == 0) n = 1; // Default
00239     scrollDown(cuY,n);
00240 }
00241 
00242 void Screen::setMode(int m)
00243 {
00244     currentModes[m] = true;
00245     switch(m)
00246     {
00247         case MODE_Origin : cuX = 0; cuY = _topMargin; break; //FIXME: home
00248     }
00249 }
00250 
00251 void Screen::resetMode(int m)
00252 {
00253     currentModes[m] = false;
00254     switch(m)
00255     {
00256         case MODE_Origin : cuX = 0; cuY = 0; break; //FIXME: home
00257     }
00258 }
00259 
00260 void Screen::saveMode(int m)
00261 {
00262     savedModes[m] = currentModes[m];
00263 }
00264 
00265 void Screen::restoreMode(int m)
00266 {
00267     currentModes[m] = savedModes[m];
00268 }
00269 
00270 bool Screen::getMode(int m) const
00271 {
00272     return currentModes[m];
00273 }
00274 
00275 void Screen::saveCursor()
00276 {
00277     savedState.cursorColumn = cuX;
00278     savedState.cursorLine  = cuY;
00279     savedState.rendition = currentRendition;
00280     savedState.foreground = currentForeground;
00281     savedState.background = currentBackground;
00282 }
00283 
00284 void Screen::restoreCursor()
00285 {
00286     cuX     = qMin(savedState.cursorColumn,columns-1);
00287     cuY     = qMin(savedState.cursorLine,lines-1);
00288     currentRendition   = savedState.rendition; 
00289     currentForeground   = savedState.foreground;
00290     currentBackground   = savedState.background;
00291     updateEffectiveRendition();
00292 }
00293 
00294 void Screen::resizeImage(int new_lines, int new_columns)
00295 {
00296     if ((new_lines==lines) && (new_columns==columns)) return;
00297 
00298     if (cuY > new_lines-1)
00299     { // attempt to preserve focus and lines
00300         _bottomMargin = lines-1; //FIXME: margin lost
00301         for (int i = 0; i < cuY-(new_lines-1); i++)
00302         {
00303             addHistLine(); scrollUp(0,1);
00304         }
00305     }
00306 
00307     // create new screen lines and copy from old to new
00308 
00309     ImageLine* newScreenLines = new ImageLine[new_lines+1];
00310     for (int i=0; i < qMin(lines-1,new_lines+1) ;i++)
00311         newScreenLines[i]=screenLines[i];
00312     for (int i=lines;(i > 0) && (i<new_lines+1);i++)
00313         newScreenLines[i].resize( new_columns );
00314 
00315     lineProperties.resize(new_lines+1);
00316     for (int i=lines;(i > 0) && (i<new_lines+1);i++)
00317         lineProperties[i] = LINE_DEFAULT;
00318 
00319     clearSelection();
00320 
00321     delete[] screenLines; 
00322     screenLines = newScreenLines;
00323 
00324     lines = new_lines;
00325     columns = new_columns;
00326     cuX = qMin(cuX,columns-1);
00327     cuY = qMin(cuY,lines-1);
00328 
00329     // FIXME: try to keep values, evtl.
00330     _topMargin=0;
00331     _bottomMargin=lines-1;
00332     initTabStops();
00333     clearSelection();
00334 }
00335 
00336 void Screen::setDefaultMargins()
00337 {
00338     _topMargin = 0;
00339     _bottomMargin = lines-1;
00340 }
00341 
00342 
00343 /*
00344    Clarifying rendition here and in the display.
00345 
00346    currently, the display's color table is
00347    0       1       2 .. 9    10 .. 17
00348    dft_fg, dft_bg, dim 0..7, intensive 0..7
00349 
00350    currentForeground, currentBackground contain values 0..8;
00351    - 0    = default color
00352    - 1..8 = ansi specified color
00353 
00354    re_fg, re_bg contain values 0..17
00355    due to the TerminalDisplay's color table
00356 
00357    rendition attributes are
00358 
00359    attr           widget screen
00360    -------------- ------ ------
00361    RE_UNDERLINE     XX     XX    affects foreground only
00362    RE_BLINK         XX     XX    affects foreground only
00363    RE_BOLD          XX     XX    affects foreground only
00364    RE_REVERSE       --     XX
00365    RE_TRANSPARENT   XX     --    affects background only
00366    RE_INTENSIVE     XX     --    affects foreground only
00367 
00368    Note that RE_BOLD is used in both widget
00369    and screen rendition. Since xterm/vt102
00370    is to poor to distinguish between bold
00371    (which is a font attribute) and intensive
00372    (which is a color attribute), we translate
00373    this and RE_BOLD in falls eventually appart
00374    into RE_BOLD and RE_INTENSIVE.
00375    */
00376 
00377 void Screen::reverseRendition(Character& p) const
00378 { 
00379     CharacterColor f = p.foregroundColor; 
00380     CharacterColor b = p.backgroundColor;
00381 
00382     p.foregroundColor = b; 
00383     p.backgroundColor = f; //p->r &= ~RE_TRANSPARENT;
00384 }
00385 
00386 void Screen::updateEffectiveRendition()
00387 {
00388     effectiveRendition = currentRendition;
00389     if (currentRendition & RE_REVERSE)
00390     {
00391         effectiveForeground = currentBackground;
00392         effectiveBackground = currentForeground;
00393     }
00394     else
00395     {
00396         effectiveForeground = currentForeground;
00397         effectiveBackground = currentBackground;
00398     }
00399 
00400     if (currentRendition & RE_BOLD)
00401         effectiveForeground.toggleIntensive();
00402 }
00403 
00404 void Screen::copyFromHistory(Character* dest, int startLine, int count) const
00405 {
00406     Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= history->getLines() );
00407 
00408     for (int line = startLine; line < startLine + count; line++) 
00409     {
00410         const int length = qMin(columns,history->getLineLen(line));
00411         const int destLineOffset  = (line-startLine)*columns;
00412 
00413         history->getCells(line,0,length,dest + destLineOffset);
00414 
00415         for (int column = length; column < columns; column++) 
00416             dest[destLineOffset+column] = defaultChar;
00417 
00418         // invert selected text
00419         if (selBegin !=-1)
00420         {
00421             for (int column = 0; column < columns; column++)
00422             {
00423                 if (isSelected(column,line)) 
00424                 {
00425                     reverseRendition(dest[destLineOffset + column]); 
00426                 }
00427             }
00428         }
00429     }
00430 }
00431 
00432 void Screen::copyFromScreen(Character* dest , int startLine , int count) const
00433 {
00434     Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= lines );
00435 
00436     for (int line = startLine; line < (startLine+count) ; line++)
00437     {
00438         int srcLineStartIndex  = line*columns;
00439         int destLineStartIndex = (line-startLine)*columns;
00440 
00441         for (int column = 0; column < columns; column++)
00442         { 
00443             int srcIndex = srcLineStartIndex + column; 
00444             int destIndex = destLineStartIndex + column;
00445 
00446             dest[destIndex] = screenLines[srcIndex/columns].value(srcIndex%columns,defaultChar);
00447 
00448             // invert selected text
00449             if (selBegin != -1 && isSelected(column,line + history->getLines()))
00450                 reverseRendition(dest[destIndex]); 
00451         }
00452 
00453     }
00454 }
00455 
00456 void Screen::getImage( Character* dest, int size, int startLine, int endLine ) const
00457 {
00458     Q_ASSERT( startLine >= 0 ); 
00459     Q_ASSERT( endLine >= startLine && endLine < history->getLines() + lines );
00460 
00461     const int mergedLines = endLine - startLine + 1;
00462 
00463     Q_ASSERT( size >= mergedLines * columns ); 
00464 
00465     const int linesInHistoryBuffer = qBound(0,history->getLines()-startLine,mergedLines);
00466     const int linesInScreenBuffer = mergedLines - linesInHistoryBuffer;
00467 
00468     // copy lines from history buffer
00469     if (linesInHistoryBuffer > 0)
00470         copyFromHistory(dest,startLine,linesInHistoryBuffer); 
00471 
00472     // copy lines from screen buffer
00473     if (linesInScreenBuffer > 0)
00474         copyFromScreen(dest + linesInHistoryBuffer*columns,
00475                 startLine + linesInHistoryBuffer - history->getLines(),
00476                 linesInScreenBuffer);
00477 
00478     // invert display when in screen mode
00479     if (getMode(MODE_Screen))
00480     {
00481         for (int i = 0; i < mergedLines*columns; i++)
00482             reverseRendition(dest[i]); // for reverse display
00483     }
00484 
00485     // mark the character at the current cursor position
00486     int cursorIndex = loc(cuX, cuY + linesInHistoryBuffer);
00487     if(getMode(MODE_Cursor) && cursorIndex < columns*mergedLines)
00488         dest[cursorIndex].rendition |= RE_CURSOR;
00489 }
00490 
00491 QVector<LineProperty> Screen::getLineProperties( int startLine , int endLine ) const
00492 {
00493     Q_ASSERT( startLine >= 0 ); 
00494     Q_ASSERT( endLine >= startLine && endLine < history->getLines() + lines );
00495 
00496     const int mergedLines = endLine-startLine+1;
00497     const int linesInHistory = qBound(0,history->getLines()-startLine,mergedLines);
00498     const int linesInScreen = mergedLines - linesInHistory;
00499 
00500     QVector<LineProperty> result(mergedLines);
00501     int index = 0;
00502 
00503     // copy properties for lines in history
00504     for (int line = startLine; line < startLine + linesInHistory; line++) 
00505     {
00506         //TODO Support for line properties other than wrapped lines
00507         if (history->isWrappedLine(line))
00508         {
00509             result[index] = (LineProperty)(result[index] | LINE_WRAPPED);
00510         }
00511         index++;
00512     }
00513 
00514     // copy properties for lines in screen buffer
00515     const int firstScreenLine = startLine + linesInHistory - history->getLines();
00516     for (int line = firstScreenLine; line < firstScreenLine+linesInScreen; line++)
00517     {
00518         result[index]=lineProperties[line];
00519         index++;
00520     }
00521 
00522     return result;
00523 }
00524 
00525 void Screen::reset(bool clearScreen)
00526 {
00527     setMode(MODE_Wrap  ); saveMode(MODE_Wrap  );  // wrap at end of margin
00528     resetMode(MODE_Origin); saveMode(MODE_Origin);  // position refere to [1,1]
00529     resetMode(MODE_Insert); saveMode(MODE_Insert);  // overstroke
00530     setMode(MODE_Cursor);                         // cursor visible
00531     resetMode(MODE_Screen);                         // screen not inverse
00532     resetMode(MODE_NewLine);
00533 
00534     _topMargin=0;
00535     _bottomMargin=lines-1;
00536 
00537     setDefaultRendition();
00538     saveCursor();
00539 
00540     if ( clearScreen )
00541         clear();
00542 }
00543 
00544 void Screen::clear()
00545 {
00546     clearEntireScreen();
00547     home();
00548 }
00549 
00550 void Screen::backspace()
00551 {
00552     cuX = qMin(columns-1,cuX); // nowrap!
00553     cuX = qMax(0,cuX-1);
00554 
00555     if (screenLines[cuY].size() < cuX+1)
00556         screenLines[cuY].resize(cuX+1);
00557 
00558     if (BS_CLEARS) 
00559         screenLines[cuY][cuX].character = ' ';
00560 }
00561 
00562 void Screen::tab(int n)
00563 {
00564     // note that TAB is a format effector (does not write ' ');
00565     if (n == 0) n = 1;
00566     while((n > 0) && (cuX < columns-1))
00567     {
00568         cursorRight(1); 
00569         while((cuX < columns-1) && !tabStops[cuX]) 
00570             cursorRight(1);
00571         n--;
00572     }
00573 }
00574 
00575 void Screen::backtab(int n)
00576 {
00577     // note that TAB is a format effector (does not write ' ');
00578     if (n == 0) n = 1;
00579     while((n > 0) && (cuX > 0))
00580     {
00581         cursorLeft(1); while((cuX > 0) && !tabStops[cuX]) cursorLeft(1);
00582         n--;
00583     }
00584 }
00585 
00586 void Screen::clearTabStops()
00587 {
00588     for (int i = 0; i < columns; i++) tabStops[i] = false;
00589 }
00590 
00591 void Screen::changeTabStop(bool set)
00592 {
00593     if (cuX >= columns) return;
00594     tabStops[cuX] = set;
00595 }
00596 
00597 void Screen::initTabStops()
00598 {
00599     tabStops.resize(columns);
00600 
00601     // Arrg! The 1st tabstop has to be one longer than the other.
00602     // i.e. the kids start counting from 0 instead of 1.
00603     // Other programs might behave correctly. Be aware.
00604     for (int i = 0; i < columns; i++) 
00605         tabStops[i] = (i%8 == 0 && i != 0);
00606 }
00607 
00608 void Screen::newLine()
00609 {
00610     if (getMode(MODE_NewLine)) 
00611         toStartOfLine();
00612     index();
00613 }
00614 
00615 void Screen::checkSelection(int from, int to)
00616 {
00617     if (selBegin == -1) 
00618         return;
00619     int scr_TL = loc(0, history->getLines());
00620     //Clear entire selection if it overlaps region [from, to]
00621     if ( (selBottomRight > (from+scr_TL)) && (selTopLeft < (to+scr_TL)) )
00622         clearSelection();
00623 }
00624 
00625 void Screen::displayCharacter(unsigned short c)
00626 {
00627     // Note that VT100 does wrapping BEFORE putting the character.
00628     // This has impact on the assumption of valid cursor positions.
00629     // We indicate the fact that a newline has to be triggered by
00630     // putting the cursor one right to the last column of the screen.
00631 
00632     int w = konsole_wcwidth(c);
00633     if (w <= 0)
00634         return;
00635 
00636     if (cuX+w > columns) {
00637         if (getMode(MODE_Wrap)) {
00638             lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | LINE_WRAPPED);
00639             nextLine();
00640         }
00641         else
00642             cuX = columns-w;
00643     }
00644 
00645     // ensure current line vector has enough elements
00646     int size = screenLines[cuY].size();
00647     if (size == 0 && cuY > 0)
00648     {
00649         screenLines[cuY].resize( qMax(screenLines[cuY-1].size() , cuX+w) );
00650     }
00651     else
00652     {
00653         if (size < cuX+w)
00654         {
00655             screenLines[cuY].resize(cuX+w);
00656         }
00657     }
00658 
00659     if (getMode(MODE_Insert)) insertChars(w);
00660 
00661     lastPos = loc(cuX,cuY);
00662 
00663     // check if selection is still valid.
00664     checkSelection(cuX,cuY);
00665 
00666     Character& currentChar = screenLines[cuY][cuX];
00667 
00668     currentChar.character = c;
00669     currentChar.foregroundColor = effectiveForeground;
00670     currentChar.backgroundColor = effectiveBackground;
00671     currentChar.rendition = effectiveRendition;
00672 
00673     int i = 0;
00674     int newCursorX = cuX + w--;
00675     while(w)
00676     {
00677         i++;
00678 
00679         if ( screenLines[cuY].size() < cuX + i + 1 )
00680             screenLines[cuY].resize(cuX+i+1);
00681 
00682         Character& ch = screenLines[cuY][cuX + i];
00683         ch.character = 0;
00684         ch.foregroundColor = effectiveForeground;
00685         ch.backgroundColor = effectiveBackground;
00686         ch.rendition = effectiveRendition;
00687 
00688         w--;
00689     }
00690     cuX = newCursorX;
00691 }
00692 
00693 void Screen::compose(const QString& /*compose*/)
00694 {
00695     Q_ASSERT( 0 /*Not implemented yet*/ );
00696 
00697     /*  if (lastPos == -1)
00698         return;
00699 
00700         QChar c(image[lastPos].character);
00701         compose.prepend(c);
00702     //compose.compose(); ### FIXME!
00703     image[lastPos].character = compose[0].unicode();*/
00704 }
00705 
00706 int Screen::scrolledLines() const
00707 {
00708     return _scrolledLines;
00709 }
00710 int Screen::droppedLines() const
00711 {
00712     return _droppedLines;
00713 }
00714 void Screen::resetDroppedLines()
00715 {
00716     _droppedLines = 0;
00717 }
00718 void Screen::resetScrolledLines()
00719 {
00720     _scrolledLines = 0;
00721 }
00722 
00723 void Screen::scrollUp(int n)
00724 {
00725     if (n == 0) n = 1; // Default
00726     if (_topMargin == 0) addHistLine(); // history.history
00727     scrollUp(_topMargin, n);
00728 }
00729 
00730 QRect Screen::lastScrolledRegion() const
00731 {
00732     return _lastScrolledRegion;
00733 }
00734 
00735 void Screen::scrollUp(int from, int n)
00736 {
00737     if (n <= 0 || from + n > _bottomMargin) return;
00738 
00739     _scrolledLines -= n;
00740     _lastScrolledRegion = QRect(0,_topMargin,columns-1,(_bottomMargin-_topMargin));
00741 
00742     //FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds.
00743     moveImage(loc(0,from),loc(0,from+n),loc(columns-1,_bottomMargin));
00744     clearImage(loc(0,_bottomMargin-n+1),loc(columns-1,_bottomMargin),' ');
00745 }
00746 
00747 void Screen::scrollDown(int n)
00748 {
00749     if (n == 0) n = 1; // Default
00750     scrollDown(_topMargin, n);
00751 }
00752 
00753 void Screen::scrollDown(int from, int n)
00754 {
00755     _scrolledLines += n;
00756 
00757     //FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds.
00758     if (n <= 0) 
00759         return;
00760     if (from > _bottomMargin) 
00761         return;
00762     if (from + n > _bottomMargin) 
00763         n = _bottomMargin - from;
00764     moveImage(loc(0,from+n),loc(0,from),loc(columns-1,_bottomMargin-n));
00765     clearImage(loc(0,from),loc(columns-1,from+n-1),' ');
00766 }
00767 
00768 void Screen::setCursorYX(int y, int x)
00769 {
00770     setCursorY(y); setCursorX(x);
00771 }
00772 
00773 void Screen::setCursorX(int x)
00774 {
00775     if (x == 0) x = 1; // Default
00776     x -= 1; // Adjust
00777     cuX = qMax(0,qMin(columns-1, x));
00778 }
00779 
00780 void Screen::setCursorY(int y)
00781 {
00782     if (y == 0) y = 1; // Default
00783     y -= 1; // Adjust
00784     cuY = qMax(0,qMin(lines  -1, y + (getMode(MODE_Origin) ? _topMargin : 0) ));
00785 }
00786 
00787 void Screen::home()
00788 {
00789     cuX = 0;
00790     cuY = 0;
00791 }
00792 
00793 void Screen::toStartOfLine()
00794 {
00795     cuX = 0;
00796 }
00797 
00798 int Screen::getCursorX() const
00799 {
00800     return cuX;
00801 }
00802 
00803 int Screen::getCursorY() const
00804 {
00805     return cuY;
00806 }
00807 
00808 void Screen::clearImage(int loca, int loce, char c)
00809 { 
00810     int scr_TL=loc(0,history->getLines());
00811     //FIXME: check positions
00812 
00813     //Clear entire selection if it overlaps region to be moved...
00814     if ( (selBottomRight > (loca+scr_TL) )&&(selTopLeft < (loce+scr_TL)) )
00815     {
00816         clearSelection();
00817     }
00818 
00819     int topLine = loca/columns;
00820     int bottomLine = loce/columns;
00821 
00822     Character clearCh(c,currentForeground,currentBackground,DEFAULT_RENDITION);
00823 
00824     //if the character being used to clear the area is the same as the
00825     //default character, the affected lines can simply be shrunk.
00826     bool isDefaultCh = (clearCh == Character());
00827 
00828     for (int y=topLine;y<=bottomLine;y++)
00829     {
00830         lineProperties[y] = 0;
00831 
00832         int endCol = ( y == bottomLine) ? loce%columns : columns-1;
00833         int startCol = ( y == topLine ) ? loca%columns : 0;
00834 
00835         QVector<Character>& line = screenLines[y];
00836 
00837         if ( isDefaultCh && endCol == columns-1 )
00838         {
00839             line.resize(startCol);
00840         }
00841         else
00842         {
00843             if (line.size() < endCol + 1)
00844                 line.resize(endCol+1);
00845 
00846             Character* data = line.data();
00847             for (int i=startCol;i<=endCol;i++)
00848                 data[i]=clearCh;
00849         }
00850     }
00851 }
00852 
00853 void Screen::moveImage(int dest, int sourceBegin, int sourceEnd)
00854 {
00855     Q_ASSERT( sourceBegin <= sourceEnd );
00856 
00857     int lines=(sourceEnd-sourceBegin)/columns;
00858 
00859     //move screen image and line properties:
00860     //the source and destination areas of the image may overlap, 
00861     //so it matters that we do the copy in the right order - 
00862     //forwards if dest < sourceBegin or backwards otherwise.
00863     //(search the web for 'memmove implementation' for details)
00864     if (dest < sourceBegin)
00865     {
00866         for (int i=0;i<=lines;i++)
00867         {
00868             screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ];
00869             lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i];
00870         }
00871     }
00872     else
00873     {
00874         for (int i=lines;i>=0;i--)
00875         {
00876             screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ];
00877             lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i];
00878         }
00879     }
00880 
00881     if (lastPos != -1)
00882     {
00883         int diff = dest - sourceBegin; // Scroll by this amount
00884         lastPos += diff;
00885         if ((lastPos < 0) || (lastPos >= (lines*columns)))
00886             lastPos = -1;
00887     }
00888 
00889     // Adjust selection to follow scroll.
00890     if (selBegin != -1)
00891     {
00892         bool beginIsTL = (selBegin == selTopLeft);
00893         int diff = dest - sourceBegin; // Scroll by this amount
00894         int scr_TL=loc(0,history->getLines());
00895         int srca = sourceBegin+scr_TL; // Translate index from screen to global
00896         int srce = sourceEnd+scr_TL; // Translate index from screen to global
00897         int desta = srca+diff;
00898         int deste = srce+diff;
00899 
00900         if ((selTopLeft >= srca) && (selTopLeft <= srce))
00901             selTopLeft += diff;
00902         else if ((selTopLeft >= desta) && (selTopLeft <= deste))
00903             selBottomRight = -1; // Clear selection (see below)
00904 
00905         if ((selBottomRight >= srca) && (selBottomRight <= srce))
00906             selBottomRight += diff;
00907         else if ((selBottomRight >= desta) && (selBottomRight <= deste))
00908             selBottomRight = -1; // Clear selection (see below)
00909 
00910         if (selBottomRight < 0)
00911         {
00912             clearSelection();
00913         }
00914         else
00915         {
00916             if (selTopLeft < 0)
00917                 selTopLeft = 0;
00918         }
00919 
00920         if (beginIsTL)
00921             selBegin = selTopLeft;
00922         else
00923             selBegin = selBottomRight;
00924     }
00925 }
00926 
00927 void Screen::clearToEndOfScreen()
00928 {
00929     clearImage(loc(cuX,cuY),loc(columns-1,lines-1),' ');
00930 }
00931 
00932 void Screen::clearToBeginOfScreen()
00933 {
00934     clearImage(loc(0,0),loc(cuX,cuY),' ');
00935 }
00936 
00937 void Screen::clearEntireScreen()
00938 {
00939     // Add entire screen to history
00940     for (int i = 0; i < (lines-1); i++)
00941     {
00942         addHistLine(); scrollUp(0,1);
00943     }
00944 
00945     clearImage(loc(0,0),loc(columns-1,lines-1),' ');
00946 }
00947 
00952 void Screen::helpAlign()
00953 {
00954     clearImage(loc(0,0),loc(columns-1,lines-1),'E');
00955 }
00956 
00957 void Screen::clearToEndOfLine()
00958 {
00959     clearImage(loc(cuX,cuY),loc(columns-1,cuY),' ');
00960 }
00961 
00962 void Screen::clearToBeginOfLine()
00963 {
00964     clearImage(loc(0,cuY),loc(cuX,cuY),' ');
00965 }
00966 
00967 void Screen::clearEntireLine()
00968 {
00969     clearImage(loc(0,cuY),loc(columns-1,cuY),' ');
00970 }
00971 
00972 void Screen::setRendition(int re)
00973 {
00974     currentRendition |= re;
00975     updateEffectiveRendition();
00976 }
00977 
00978 void Screen::resetRendition(int re)
00979 {
00980     currentRendition &= ~re;
00981     updateEffectiveRendition();
00982 }
00983 
00984 void Screen::setDefaultRendition()
00985 {
00986     setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR);
00987     setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR);
00988     currentRendition   = DEFAULT_RENDITION;
00989     updateEffectiveRendition();
00990 }
00991 
00992 void Screen::setForeColor(int space, int color)
00993 {
00994     currentForeground = CharacterColor(space, color);
00995 
00996     if ( currentForeground.isValid() ) 
00997         updateEffectiveRendition();
00998     else 
00999         setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR);
01000 }
01001 
01002 void Screen::setBackColor(int space, int color)
01003 {
01004     currentBackground = CharacterColor(space, color);
01005 
01006     if ( currentBackground.isValid() ) 
01007         updateEffectiveRendition();
01008     else
01009         setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR);
01010 }
01011 
01012 void Screen::clearSelection() 
01013 {
01014     selBottomRight = -1;
01015     selTopLeft = -1;
01016     selBegin = -1;
01017 }
01018 
01019 void Screen::getSelectionStart(int& column , int& line) const
01020 {
01021     if ( selTopLeft != -1 )
01022     {
01023         column = selTopLeft % columns;
01024         line = selTopLeft / columns; 
01025     }
01026     else
01027     {
01028         column = cuX + getHistLines();
01029         line = cuY + getHistLines();
01030     }
01031 }
01032 void Screen::getSelectionEnd(int& column , int& line) const
01033 {
01034     if ( selBottomRight != -1 )
01035     {
01036         column = selBottomRight % columns;
01037         line = selBottomRight / columns;
01038     }
01039     else
01040     {
01041         column = cuX + getHistLines();
01042         line = cuY + getHistLines();
01043     } 
01044 }
01045 void Screen::setSelectionStart(const int x, const int y, const bool mode)
01046 {
01047     selBegin = loc(x,y); 
01048     /* FIXME, HACK to correct for x too far to the right... */
01049     if (x == columns) selBegin--;
01050 
01051     selBottomRight = selBegin;
01052     selTopLeft = selBegin;
01053     blockSelectionMode = mode;
01054 }
01055 
01056 void Screen::setSelectionEnd( const int x, const int y)
01057 {
01058     if (selBegin == -1) 
01059         return;
01060 
01061     int endPos =  loc(x,y); 
01062 
01063     if (endPos < selBegin)
01064     {
01065         selTopLeft = endPos;
01066         selBottomRight = selBegin;
01067     }
01068     else
01069     {
01070         /* FIXME, HACK to correct for x too far to the right... */
01071         if (x == columns) 
01072             endPos--;
01073 
01074         selTopLeft = selBegin;
01075         selBottomRight = endPos;
01076     }
01077 
01078     // Normalize the selection in column mode
01079     if (blockSelectionMode)
01080     {
01081         int topRow = selTopLeft / columns;
01082         int topColumn = selTopLeft % columns;
01083         int bottomRow = selBottomRight / columns;
01084         int bottomColumn = selBottomRight % columns;
01085 
01086         selTopLeft = loc(qMin(topColumn,bottomColumn),topRow);
01087         selBottomRight = loc(qMax(topColumn,bottomColumn),bottomRow);
01088     }
01089 }
01090 
01091 bool Screen::isSelected( const int x,const int y) const
01092 {
01093     bool columnInSelection = true;
01094     if (blockSelectionMode)
01095     {
01096         columnInSelection = x >= (selTopLeft % columns) &&
01097             x <= (selBottomRight % columns);
01098     }
01099 
01100     int pos = loc(x,y);
01101     return pos >= selTopLeft && pos <= selBottomRight && columnInSelection;
01102 }
01103 
01104 QString Screen::selectedText(bool preserveLineBreaks) const
01105 {
01106     QString result;
01107     QTextStream stream(&result, QIODevice::ReadWrite);
01108 
01109     PlainTextDecoder decoder;
01110     decoder.begin(&stream);
01111     writeSelectionToStream(&decoder , preserveLineBreaks);
01112     decoder.end();
01113 
01114     return result;
01115 }
01116 
01117 bool Screen::isSelectionValid() const
01118 {
01119     return selTopLeft >= 0 && selBottomRight >= 0;
01120 }
01121 
01122 void Screen::writeSelectionToStream(TerminalCharacterDecoder* decoder , 
01123         bool preserveLineBreaks) const
01124 {
01125     if (!isSelectionValid())
01126         return;
01127     writeToStream(decoder,selTopLeft,selBottomRight,preserveLineBreaks);
01128 }
01129 
01130 void Screen::writeToStream(TerminalCharacterDecoder* decoder, 
01131         int startIndex, int endIndex,
01132         bool preserveLineBreaks) const
01133 {
01134     int top = startIndex / columns;    
01135     int left = startIndex % columns;
01136 
01137     int bottom = endIndex / columns;
01138     int right = endIndex % columns;
01139 
01140     Q_ASSERT( top >= 0 && left >= 0 && bottom >= 0 && right >= 0 );
01141 
01142     for (int y=top;y<=bottom;y++)
01143     {
01144         int start = 0;
01145         if ( y == top || blockSelectionMode ) start = left;
01146 
01147         int count = -1;
01148         if ( y == bottom || blockSelectionMode ) count = right - start + 1;
01149 
01150         const bool appendNewLine = ( y != bottom );
01151         int copied = copyLineToStream( y,
01152                 start,
01153                 count,
01154                 decoder, 
01155                 appendNewLine,
01156                 preserveLineBreaks );
01157 
01158         // if the selection goes beyond the end of the last line then
01159         // append a new line character.
01160         //
01161         // this makes it possible to 'select' a trailing new line character after
01162         // the text on a line.  
01163         if ( y == bottom && 
01164                 copied < count    )
01165         {
01166             Character newLineChar('\n');
01167             decoder->decodeLine(&newLineChar,1,0);
01168         }
01169     }    
01170 }
01171 
01172 int Screen::copyLineToStream(int line , 
01173         int start, 
01174         int count,
01175         TerminalCharacterDecoder* decoder,
01176         bool appendNewLine,
01177         bool preserveLineBreaks) const
01178 {
01179     //buffer to hold characters for decoding
01180     //the buffer is static to avoid initialising every 
01181     //element on each call to copyLineToStream
01182     //(which is unnecessary since all elements will be overwritten anyway)
01183     static const int MAX_CHARS = 1024;
01184     static Character characterBuffer[MAX_CHARS];
01185 
01186     assert( count < MAX_CHARS );
01187 
01188     LineProperty currentLineProperties = 0;
01189 
01190     //determine if the line is in the history buffer or the screen image
01191     if (line < history->getLines())
01192     {
01193         const int lineLength = history->getLineLen(line);
01194 
01195         // ensure that start position is before end of line
01196         start = qMin(start,qMax(0,lineLength-1));
01197 
01198         // retrieve line from history buffer.  It is assumed
01199         // that the history buffer does not store trailing white space
01200         // at the end of the line, so it does not need to be trimmed here 
01201         if (count == -1)
01202         {
01203             count = lineLength-start;
01204         }
01205         else
01206         {
01207             count = qMin(start+count,lineLength)-start;
01208         }
01209 
01210         // safety checks
01211         assert( start >= 0 );
01212         assert( count >= 0 );    
01213         assert( (start+count) <= history->getLineLen(line) );
01214 
01215         history->getCells(line,start,count,characterBuffer);
01216 
01217         if ( history->isWrappedLine(line) )
01218             currentLineProperties |= LINE_WRAPPED;
01219     }
01220     else
01221     {
01222         if ( count == -1 )
01223             count = columns - start;
01224 
01225         assert( count >= 0 );
01226 
01227         const int screenLine = line-history->getLines();
01228 
01229         Character* data = screenLines[screenLine].data();
01230         int length = screenLines[screenLine].count();
01231 
01232         // ignore trailing white space at the end of the line
01233         for (int i = length-1; i >= 0; i--)
01234         {
01235             if (data[i].character == ' ')
01236                 length--;
01237             else
01238                 break;
01239         }
01240         //retrieve line from screen image
01241         for (int i=start;i < qMin(start+count,length);i++)
01242         {
01243             characterBuffer[i-start] = data[i];
01244         }
01245 
01246         // count cannot be any greater than length
01247         count = qBound(0,count,length-start);
01248 
01249         Q_ASSERT( screenLine < lineProperties.count() );
01250         currentLineProperties |= lineProperties[screenLine]; 
01251     }
01252 
01253     // add new line character at end
01254     const bool omitLineBreak = (currentLineProperties & LINE_WRAPPED) ||
01255         !preserveLineBreaks;
01256 
01257     if ( !omitLineBreak && appendNewLine && (count+1 < MAX_CHARS) )
01258     {
01259         characterBuffer[count] = '\n';
01260         count++;
01261     }
01262 
01263     //decode line and write to text stream    
01264     decoder->decodeLine( (Character*) characterBuffer , 
01265             count, currentLineProperties );
01266 
01267     return count;
01268 }
01269 
01270 void Screen::writeLinesToStream(TerminalCharacterDecoder* decoder, int fromLine, int toLine) const
01271 {
01272     writeToStream(decoder,loc(0,fromLine),loc(columns-1,toLine));
01273 }
01274 
01275 void Screen::addHistLine()
01276 {
01277     // add line to history buffer
01278     // we have to take care about scrolling, too...
01279 
01280     if (hasScroll())
01281     {
01282         int oldHistLines = history->getLines();
01283 
01284         history->addCellsVector(screenLines[0]);
01285         history->addLine( lineProperties[0] & LINE_WRAPPED );
01286 
01287         int newHistLines = history->getLines();
01288 
01289         bool beginIsTL = (selBegin == selTopLeft);
01290 
01291         // If the history is full, increment the count
01292         // of dropped lines
01293         if ( newHistLines == oldHistLines )
01294             _droppedLines++;
01295 
01296         // Adjust selection for the new point of reference
01297         if (newHistLines > oldHistLines)
01298         {
01299             if (selBegin != -1)
01300             {
01301                 selTopLeft += columns;
01302                 selBottomRight += columns;
01303             }
01304         }
01305 
01306         if (selBegin != -1)
01307         {
01308             // Scroll selection in history up
01309             int top_BR = loc(0, 1+newHistLines);
01310 
01311             if (selTopLeft < top_BR)
01312                 selTopLeft -= columns;
01313 
01314             if (selBottomRight < top_BR)
01315                 selBottomRight -= columns;
01316 
01317             if (selBottomRight < 0)
01318                 clearSelection();
01319             else
01320             {
01321                 if (selTopLeft < 0)
01322                     selTopLeft = 0;
01323             }
01324 
01325             if (beginIsTL)
01326                 selBegin = selTopLeft;
01327             else
01328                 selBegin = selBottomRight;
01329         }
01330     }
01331 
01332 }
01333 
01334 int Screen::getHistLines() const
01335 {
01336     return history->getLines();
01337 }
01338 
01339 void Screen::setScroll(const HistoryType& t , bool copyPreviousScroll)
01340 {
01341     clearSelection();
01342 
01343     if ( copyPreviousScroll )
01344         history = t.scroll(history);
01345     else
01346     {
01347         HistoryScroll* oldScroll = history;
01348         history = t.scroll(0);
01349         delete oldScroll;
01350     }
01351 }
01352 
01353 bool Screen::hasScroll() const
01354 {
01355     return history->hasScroll();
01356 }
01357 
01358 const HistoryType& Screen::getScroll() const
01359 {
01360     return history->getType();
01361 }
01362 
01363 void Screen::setLineProperty(LineProperty property , bool enable)
01364 {
01365     if ( enable )
01366         lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | property);
01367     else
01368         lineProperties[cuY] = (LineProperty)(lineProperties[cuY] & ~property);
01369 }
01370 void Screen::fillWithDefaultChar(Character* dest, int count)
01371 {
01372     for (int i=0;i<count;i++)
01373         dest[i] = defaultChar;
01374 }

Konsole

Skip menu "Konsole"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • Konsole
  • Libraries
  •   libkonq
Generated for API Reference by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal