add more spacing
[personal-kdebase.git] / apps / konsole / src / Screen.cpp
blob493302a8e196733dbb0404d4425f3415453e3749
1 /*
2 This file is part of Konsole, an X terminal.
4 Copyright 2007-2008 by Robert Knight <robert.knight@gmail.com>
5 Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
23 // Own
24 #include "Screen.h"
26 // Standard
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <assert.h>
31 #include <string.h>
32 #include <ctype.h>
34 // Qt
35 #include <QtCore/QTextStream>
36 #include <QtCore/QDate>
38 // KDE
39 #include <kdebug.h>
41 // Konsole
42 #include "konsole_wcwidth.h"
43 #include "TerminalCharacterDecoder.h"
45 using namespace Konsole;
47 //FIXME: this is emulation specific. Use false for xterm, true for ANSI.
48 //FIXME: see if we can get this from terminfo.
49 #define BS_CLEARS false
51 //Macro to convert x,y position on screen to position within an image.
53 //Originally the image was stored as one large contiguous block of
54 //memory, so a position within the image could be represented as an
55 //offset from the beginning of the block. For efficiency reasons this
56 //is no longer the case.
57 //Many internal parts of this class still use this representation for parameters and so on,
58 //notably moveImage() and clearImage().
59 //This macro converts from an X,Y position into an image offset.
60 #ifndef loc
61 #define loc(X,Y) ((Y)*columns+(X))
62 #endif
65 Character Screen::defaultChar = Character(' ',
66 CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR),
67 CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR),
68 DEFAULT_RENDITION);
70 //#define REVERSE_WRAPPED_LINES // for wrapped line debug
72 Screen::Screen(int l, int c)
73 : lines(l),
74 columns(c),
75 screenLines(new ImageLine[lines+1] ),
76 _scrolledLines(0),
77 _droppedLines(0),
78 history(new HistoryScrollNone()),
79 cuX(0), cuY(0),
80 currentRendition(0),
81 _topMargin(0), _bottomMargin(0),
82 selBegin(0), selTopLeft(0), selBottomRight(0),
83 blockSelectionMode(false),
84 effectiveForeground(CharacterColor()), effectiveBackground(CharacterColor()), effectiveRendition(0),
85 lastPos(-1)
87 lineProperties.resize(lines+1);
88 for (int i=0;i<lines+1;i++)
89 lineProperties[i]=LINE_DEFAULT;
91 initTabStops();
92 clearSelection();
93 reset();
96 /*! Destructor
99 Screen::~Screen()
101 delete[] screenLines;
102 delete history;
105 void Screen::cursorUp(int n)
106 //=CUU
108 if (n == 0) n = 1; // Default
109 int stop = cuY < _topMargin ? 0 : _topMargin;
110 cuX = qMin(columns-1,cuX); // nowrap!
111 cuY = qMax(stop,cuY-n);
114 void Screen::cursorDown(int n)
115 //=CUD
117 if (n == 0) n = 1; // Default
118 int stop = cuY > _bottomMargin ? lines-1 : _bottomMargin;
119 cuX = qMin(columns-1,cuX); // nowrap!
120 cuY = qMin(stop,cuY+n);
123 void Screen::cursorLeft(int n)
124 //=CUB
126 if (n == 0) n = 1; // Default
127 cuX = qMin(columns-1,cuX); // nowrap!
128 cuX = qMax(0,cuX-n);
131 void Screen::cursorRight(int n)
132 //=CUF
134 if (n == 0) n = 1; // Default
135 cuX = qMin(columns-1,cuX+n);
138 void Screen::setMargins(int top, int bot)
139 //=STBM
141 if (top == 0) top = 1; // Default
142 if (bot == 0) bot = lines; // Default
143 top = top - 1; // Adjust to internal lineno
144 bot = bot - 1; // Adjust to internal lineno
145 if ( !( 0 <= top && top < bot && bot < lines ) )
146 { kDebug()<<" setRegion("<<top<<","<<bot<<") : bad range.";
147 return; // Default error action: ignore
149 _topMargin = top;
150 _bottomMargin = bot;
151 cuX = 0;
152 cuY = getMode(MODE_Origin) ? top : 0;
156 int Screen::topMargin() const
158 return _topMargin;
160 int Screen::bottomMargin() const
162 return _bottomMargin;
165 void Screen::index()
166 //=IND
168 if (cuY == _bottomMargin)
169 scrollUp(1);
170 else if (cuY < lines-1)
171 cuY += 1;
174 void Screen::reverseIndex()
175 //=RI
177 if (cuY == _topMargin)
178 scrollDown(_topMargin,1);
179 else if (cuY > 0)
180 cuY -= 1;
183 void Screen::nextLine()
184 //=NEL
186 toStartOfLine(); index();
189 void Screen::eraseChars(int n)
191 if (n == 0) n = 1; // Default
192 int p = qMax(0,qMin(cuX+n-1,columns-1));
193 clearImage(loc(cuX,cuY),loc(p,cuY),' ');
196 void Screen::deleteChars(int n)
198 Q_ASSERT( n >= 0 );
200 // always delete at least one char
201 if (n == 0)
202 n = 1;
204 // if cursor is beyond the end of the line there is nothing to do
205 if ( cuX >= screenLines[cuY].count() )
206 return;
208 if ( cuX+n >= screenLines[cuY].count() )
209 n = screenLines[cuY].count() - 1 - cuX;
211 Q_ASSERT( n >= 0 );
212 Q_ASSERT( cuX+n < screenLines[cuY].count() );
214 screenLines[cuY].remove(cuX,n);
217 void Screen::insertChars(int n)
219 if (n == 0) n = 1; // Default
221 if ( screenLines[cuY].size() < cuX )
222 screenLines[cuY].resize(cuX);
224 screenLines[cuY].insert(cuX,n,' ');
226 if ( screenLines[cuY].count() > columns )
227 screenLines[cuY].resize(columns);
230 void Screen::deleteLines(int n)
232 if (n == 0) n = 1; // Default
233 scrollUp(cuY,n);
236 void Screen::insertLines(int n)
238 if (n == 0) n = 1; // Default
239 scrollDown(cuY,n);
242 void Screen::setMode(int m)
244 currentModes[m] = true;
245 switch(m)
247 case MODE_Origin : cuX = 0; cuY = _topMargin; break; //FIXME: home
251 void Screen::resetMode(int m)
253 currentModes[m] = false;
254 switch(m)
256 case MODE_Origin : cuX = 0; cuY = 0; break; //FIXME: home
260 void Screen::saveMode(int m)
262 savedModes[m] = currentModes[m];
265 void Screen::restoreMode(int m)
267 currentModes[m] = savedModes[m];
270 bool Screen::getMode(int m) const
272 return currentModes[m];
275 void Screen::saveCursor()
277 savedState.cursorColumn = cuX;
278 savedState.cursorLine = cuY;
279 savedState.rendition = currentRendition;
280 savedState.foreground = currentForeground;
281 savedState.background = currentBackground;
284 void Screen::restoreCursor()
286 cuX = qMin(savedState.cursorColumn,columns-1);
287 cuY = qMin(savedState.cursorLine,lines-1);
288 currentRendition = savedState.rendition;
289 currentForeground = savedState.foreground;
290 currentBackground = savedState.background;
291 updateEffectiveRendition();
294 void Screen::resizeImage(int new_lines, int new_columns)
296 if ((new_lines==lines) && (new_columns==columns)) return;
298 if (cuY > new_lines-1)
299 { // attempt to preserve focus and lines
300 _bottomMargin = lines-1; //FIXME: margin lost
301 for (int i = 0; i < cuY-(new_lines-1); i++)
303 addHistLine(); scrollUp(0,1);
307 // create new screen lines and copy from old to new
309 ImageLine* newScreenLines = new ImageLine[new_lines+1];
310 for (int i=0; i < qMin(lines-1,new_lines+1) ;i++)
311 newScreenLines[i]=screenLines[i];
312 for (int i=lines;(i > 0) && (i<new_lines+1);i++)
313 newScreenLines[i].resize( new_columns );
315 lineProperties.resize(new_lines+1);
316 for (int i=lines;(i > 0) && (i<new_lines+1);i++)
317 lineProperties[i] = LINE_DEFAULT;
319 clearSelection();
321 delete[] screenLines;
322 screenLines = newScreenLines;
324 lines = new_lines;
325 columns = new_columns;
326 cuX = qMin(cuX,columns-1);
327 cuY = qMin(cuY,lines-1);
329 // FIXME: try to keep values, evtl.
330 _topMargin=0;
331 _bottomMargin=lines-1;
332 initTabStops();
333 clearSelection();
336 void Screen::setDefaultMargins()
338 _topMargin = 0;
339 _bottomMargin = lines-1;
344 Clarifying rendition here and in the display.
346 currently, the display's color table is
347 0 1 2 .. 9 10 .. 17
348 dft_fg, dft_bg, dim 0..7, intensive 0..7
350 currentForeground, currentBackground contain values 0..8;
351 - 0 = default color
352 - 1..8 = ansi specified color
354 re_fg, re_bg contain values 0..17
355 due to the TerminalDisplay's color table
357 rendition attributes are
359 attr widget screen
360 -------------- ------ ------
361 RE_UNDERLINE XX XX affects foreground only
362 RE_BLINK XX XX affects foreground only
363 RE_BOLD XX XX affects foreground only
364 RE_REVERSE -- XX
365 RE_TRANSPARENT XX -- affects background only
366 RE_INTENSIVE XX -- affects foreground only
368 Note that RE_BOLD is used in both widget
369 and screen rendition. Since xterm/vt102
370 is to poor to distinguish between bold
371 (which is a font attribute) and intensive
372 (which is a color attribute), we translate
373 this and RE_BOLD in falls eventually appart
374 into RE_BOLD and RE_INTENSIVE.
377 void Screen::reverseRendition(Character& p) const
379 CharacterColor f = p.foregroundColor;
380 CharacterColor b = p.backgroundColor;
382 p.foregroundColor = b;
383 p.backgroundColor = f; //p->r &= ~RE_TRANSPARENT;
386 void Screen::updateEffectiveRendition()
388 effectiveRendition = currentRendition;
389 if (currentRendition & RE_REVERSE)
391 effectiveForeground = currentBackground;
392 effectiveBackground = currentForeground;
394 else
396 effectiveForeground = currentForeground;
397 effectiveBackground = currentBackground;
400 if (currentRendition & RE_BOLD)
401 effectiveForeground.toggleIntensive();
404 void Screen::copyFromHistory(Character* dest, int startLine, int count) const
406 Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= history->getLines() );
408 for (int line = startLine; line < startLine + count; line++)
410 const int length = qMin(columns,history->getLineLen(line));
411 const int destLineOffset = (line-startLine)*columns;
413 history->getCells(line,0,length,dest + destLineOffset);
415 for (int column = length; column < columns; column++)
416 dest[destLineOffset+column] = defaultChar;
418 // invert selected text
419 if (selBegin !=-1)
421 for (int column = 0; column < columns; column++)
423 if (isSelected(column,line))
425 reverseRendition(dest[destLineOffset + column]);
432 void Screen::copyFromScreen(Character* dest , int startLine , int count) const
434 Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= lines );
436 for (int line = startLine; line < (startLine+count) ; line++)
438 int srcLineStartIndex = line*columns;
439 int destLineStartIndex = (line-startLine)*columns;
441 for (int column = 0; column < columns; column++)
443 int srcIndex = srcLineStartIndex + column;
444 int destIndex = destLineStartIndex + column;
446 dest[destIndex] = screenLines[srcIndex/columns].value(srcIndex%columns,defaultChar);
448 // invert selected text
449 if (selBegin != -1 && isSelected(column,line + history->getLines()))
450 reverseRendition(dest[destIndex]);
456 void Screen::getImage( Character* dest, int size, int startLine, int endLine ) const
458 Q_ASSERT( startLine >= 0 );
459 Q_ASSERT( endLine >= startLine && endLine < history->getLines() + lines );
461 const int mergedLines = endLine - startLine + 1;
463 Q_ASSERT( size >= mergedLines * columns );
465 const int linesInHistoryBuffer = qBound(0,history->getLines()-startLine,mergedLines);
466 const int linesInScreenBuffer = mergedLines - linesInHistoryBuffer;
468 // copy lines from history buffer
469 if (linesInHistoryBuffer > 0)
470 copyFromHistory(dest,startLine,linesInHistoryBuffer);
472 // copy lines from screen buffer
473 if (linesInScreenBuffer > 0)
474 copyFromScreen(dest + linesInHistoryBuffer*columns,
475 startLine + linesInHistoryBuffer - history->getLines(),
476 linesInScreenBuffer);
478 // invert display when in screen mode
479 if (getMode(MODE_Screen))
481 for (int i = 0; i < mergedLines*columns; i++)
482 reverseRendition(dest[i]); // for reverse display
485 // mark the character at the current cursor position
486 int cursorIndex = loc(cuX, cuY + linesInHistoryBuffer);
487 if(getMode(MODE_Cursor) && cursorIndex < columns*mergedLines)
488 dest[cursorIndex].rendition |= RE_CURSOR;
491 QVector<LineProperty> Screen::getLineProperties( int startLine , int endLine ) const
493 Q_ASSERT( startLine >= 0 );
494 Q_ASSERT( endLine >= startLine && endLine < history->getLines() + lines );
496 const int mergedLines = endLine-startLine+1;
497 const int linesInHistory = qBound(0,history->getLines()-startLine,mergedLines);
498 const int linesInScreen = mergedLines - linesInHistory;
500 QVector<LineProperty> result(mergedLines);
501 int index = 0;
503 // copy properties for lines in history
504 for (int line = startLine; line < startLine + linesInHistory; line++)
506 //TODO Support for line properties other than wrapped lines
507 if (history->isWrappedLine(line))
509 result[index] = (LineProperty)(result[index] | LINE_WRAPPED);
511 index++;
514 // copy properties for lines in screen buffer
515 const int firstScreenLine = startLine + linesInHistory - history->getLines();
516 for (int line = firstScreenLine; line < firstScreenLine+linesInScreen; line++)
518 result[index]=lineProperties[line];
519 index++;
522 return result;
525 void Screen::reset(bool clearScreen)
527 setMode(MODE_Wrap ); saveMode(MODE_Wrap ); // wrap at end of margin
528 resetMode(MODE_Origin); saveMode(MODE_Origin); // position refere to [1,1]
529 resetMode(MODE_Insert); saveMode(MODE_Insert); // overstroke
530 setMode(MODE_Cursor); // cursor visible
531 resetMode(MODE_Screen); // screen not inverse
532 resetMode(MODE_NewLine);
534 _topMargin=0;
535 _bottomMargin=lines-1;
537 setDefaultRendition();
538 saveCursor();
540 if ( clearScreen )
541 clear();
544 void Screen::clear()
546 clearEntireScreen();
547 home();
550 void Screen::backspace()
552 cuX = qMin(columns-1,cuX); // nowrap!
553 cuX = qMax(0,cuX-1);
555 if (screenLines[cuY].size() < cuX+1)
556 screenLines[cuY].resize(cuX+1);
558 if (BS_CLEARS)
559 screenLines[cuY][cuX].character = ' ';
562 void Screen::tab(int n)
564 // note that TAB is a format effector (does not write ' ');
565 if (n == 0) n = 1;
566 while((n > 0) && (cuX < columns-1))
568 cursorRight(1);
569 while((cuX < columns-1) && !tabStops[cuX])
570 cursorRight(1);
571 n--;
575 void Screen::backtab(int n)
577 // note that TAB is a format effector (does not write ' ');
578 if (n == 0) n = 1;
579 while((n > 0) && (cuX > 0))
581 cursorLeft(1); while((cuX > 0) && !tabStops[cuX]) cursorLeft(1);
582 n--;
586 void Screen::clearTabStops()
588 for (int i = 0; i < columns; i++) tabStops[i] = false;
591 void Screen::changeTabStop(bool set)
593 if (cuX >= columns) return;
594 tabStops[cuX] = set;
597 void Screen::initTabStops()
599 tabStops.resize(columns);
601 // Arrg! The 1st tabstop has to be one longer than the other.
602 // i.e. the kids start counting from 0 instead of 1.
603 // Other programs might behave correctly. Be aware.
604 for (int i = 0; i < columns; i++)
605 tabStops[i] = (i%8 == 0 && i != 0);
608 void Screen::newLine()
610 if (getMode(MODE_NewLine))
611 toStartOfLine();
612 index();
615 void Screen::checkSelection(int from, int to)
617 if (selBegin == -1)
618 return;
619 int scr_TL = loc(0, history->getLines());
620 //Clear entire selection if it overlaps region [from, to]
621 if ( (selBottomRight > (from+scr_TL)) && (selTopLeft < (to+scr_TL)) )
622 clearSelection();
625 void Screen::displayCharacter(unsigned short c)
627 // Note that VT100 does wrapping BEFORE putting the character.
628 // This has impact on the assumption of valid cursor positions.
629 // We indicate the fact that a newline has to be triggered by
630 // putting the cursor one right to the last column of the screen.
632 int w = konsole_wcwidth(c);
633 if (w <= 0)
634 return;
636 if (cuX+w > columns) {
637 if (getMode(MODE_Wrap)) {
638 lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | LINE_WRAPPED);
639 nextLine();
641 else
642 cuX = columns-w;
645 // ensure current line vector has enough elements
646 int size = screenLines[cuY].size();
647 if (size == 0 && cuY > 0)
649 screenLines[cuY].resize( qMax(screenLines[cuY-1].size() , cuX+w) );
651 else
653 if (size < cuX+w)
655 screenLines[cuY].resize(cuX+w);
659 if (getMode(MODE_Insert)) insertChars(w);
661 lastPos = loc(cuX,cuY);
663 // check if selection is still valid.
664 checkSelection(cuX,cuY);
666 Character& currentChar = screenLines[cuY][cuX];
668 currentChar.character = c;
669 currentChar.foregroundColor = effectiveForeground;
670 currentChar.backgroundColor = effectiveBackground;
671 currentChar.rendition = effectiveRendition;
673 int i = 0;
674 int newCursorX = cuX + w--;
675 while(w)
677 i++;
679 if ( screenLines[cuY].size() < cuX + i + 1 )
680 screenLines[cuY].resize(cuX+i+1);
682 Character& ch = screenLines[cuY][cuX + i];
683 ch.character = 0;
684 ch.foregroundColor = effectiveForeground;
685 ch.backgroundColor = effectiveBackground;
686 ch.rendition = effectiveRendition;
688 w--;
690 cuX = newCursorX;
693 void Screen::compose(const QString& /*compose*/)
695 Q_ASSERT( 0 /*Not implemented yet*/ );
697 /* if (lastPos == -1)
698 return;
700 QChar c(image[lastPos].character);
701 compose.prepend(c);
702 //compose.compose(); ### FIXME!
703 image[lastPos].character = compose[0].unicode();*/
706 int Screen::scrolledLines() const
708 return _scrolledLines;
710 int Screen::droppedLines() const
712 return _droppedLines;
714 void Screen::resetDroppedLines()
716 _droppedLines = 0;
718 void Screen::resetScrolledLines()
720 _scrolledLines = 0;
723 void Screen::scrollUp(int n)
725 if (n == 0) n = 1; // Default
726 if (_topMargin == 0) addHistLine(); // history.history
727 scrollUp(_topMargin, n);
730 QRect Screen::lastScrolledRegion() const
732 return _lastScrolledRegion;
735 void Screen::scrollUp(int from, int n)
737 if (n <= 0 || from + n > _bottomMargin) return;
739 _scrolledLines -= n;
740 _lastScrolledRegion = QRect(0,_topMargin,columns-1,(_bottomMargin-_topMargin));
742 //FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds.
743 moveImage(loc(0,from),loc(0,from+n),loc(columns-1,_bottomMargin));
744 clearImage(loc(0,_bottomMargin-n+1),loc(columns-1,_bottomMargin),' ');
747 void Screen::scrollDown(int n)
749 if (n == 0) n = 1; // Default
750 scrollDown(_topMargin, n);
753 void Screen::scrollDown(int from, int n)
755 _scrolledLines += n;
757 //FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds.
758 if (n <= 0)
759 return;
760 if (from > _bottomMargin)
761 return;
762 if (from + n > _bottomMargin)
763 n = _bottomMargin - from;
764 moveImage(loc(0,from+n),loc(0,from),loc(columns-1,_bottomMargin-n));
765 clearImage(loc(0,from),loc(columns-1,from+n-1),' ');
768 void Screen::setCursorYX(int y, int x)
770 setCursorY(y); setCursorX(x);
773 void Screen::setCursorX(int x)
775 if (x == 0) x = 1; // Default
776 x -= 1; // Adjust
777 cuX = qMax(0,qMin(columns-1, x));
780 void Screen::setCursorY(int y)
782 if (y == 0) y = 1; // Default
783 y -= 1; // Adjust
784 cuY = qMax(0,qMin(lines -1, y + (getMode(MODE_Origin) ? _topMargin : 0) ));
787 void Screen::home()
789 cuX = 0;
790 cuY = 0;
793 void Screen::toStartOfLine()
795 cuX = 0;
798 int Screen::getCursorX() const
800 return cuX;
803 int Screen::getCursorY() const
805 return cuY;
808 void Screen::clearImage(int loca, int loce, char c)
810 int scr_TL=loc(0,history->getLines());
811 //FIXME: check positions
813 //Clear entire selection if it overlaps region to be moved...
814 if ( (selBottomRight > (loca+scr_TL) )&&(selTopLeft < (loce+scr_TL)) )
816 clearSelection();
819 int topLine = loca/columns;
820 int bottomLine = loce/columns;
822 Character clearCh(c,currentForeground,currentBackground,DEFAULT_RENDITION);
824 //if the character being used to clear the area is the same as the
825 //default character, the affected lines can simply be shrunk.
826 bool isDefaultCh = (clearCh == Character());
828 for (int y=topLine;y<=bottomLine;y++)
830 lineProperties[y] = 0;
832 int endCol = ( y == bottomLine) ? loce%columns : columns-1;
833 int startCol = ( y == topLine ) ? loca%columns : 0;
835 QVector<Character>& line = screenLines[y];
837 if ( isDefaultCh && endCol == columns-1 )
839 line.resize(startCol);
841 else
843 if (line.size() < endCol + 1)
844 line.resize(endCol+1);
846 Character* data = line.data();
847 for (int i=startCol;i<=endCol;i++)
848 data[i]=clearCh;
853 void Screen::moveImage(int dest, int sourceBegin, int sourceEnd)
855 Q_ASSERT( sourceBegin <= sourceEnd );
857 int lines=(sourceEnd-sourceBegin)/columns;
859 //move screen image and line properties:
860 //the source and destination areas of the image may overlap,
861 //so it matters that we do the copy in the right order -
862 //forwards if dest < sourceBegin or backwards otherwise.
863 //(search the web for 'memmove implementation' for details)
864 if (dest < sourceBegin)
866 for (int i=0;i<=lines;i++)
868 screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ];
869 lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i];
872 else
874 for (int i=lines;i>=0;i--)
876 screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ];
877 lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i];
881 if (lastPos != -1)
883 int diff = dest - sourceBegin; // Scroll by this amount
884 lastPos += diff;
885 if ((lastPos < 0) || (lastPos >= (lines*columns)))
886 lastPos = -1;
889 // Adjust selection to follow scroll.
890 if (selBegin != -1)
892 bool beginIsTL = (selBegin == selTopLeft);
893 int diff = dest - sourceBegin; // Scroll by this amount
894 int scr_TL=loc(0,history->getLines());
895 int srca = sourceBegin+scr_TL; // Translate index from screen to global
896 int srce = sourceEnd+scr_TL; // Translate index from screen to global
897 int desta = srca+diff;
898 int deste = srce+diff;
900 if ((selTopLeft >= srca) && (selTopLeft <= srce))
901 selTopLeft += diff;
902 else if ((selTopLeft >= desta) && (selTopLeft <= deste))
903 selBottomRight = -1; // Clear selection (see below)
905 if ((selBottomRight >= srca) && (selBottomRight <= srce))
906 selBottomRight += diff;
907 else if ((selBottomRight >= desta) && (selBottomRight <= deste))
908 selBottomRight = -1; // Clear selection (see below)
910 if (selBottomRight < 0)
912 clearSelection();
914 else
916 if (selTopLeft < 0)
917 selTopLeft = 0;
920 if (beginIsTL)
921 selBegin = selTopLeft;
922 else
923 selBegin = selBottomRight;
927 void Screen::clearToEndOfScreen()
929 clearImage(loc(cuX,cuY),loc(columns-1,lines-1),' ');
932 void Screen::clearToBeginOfScreen()
934 clearImage(loc(0,0),loc(cuX,cuY),' ');
937 void Screen::clearEntireScreen()
939 // Add entire screen to history
940 for (int i = 0; i < (lines-1); i++)
942 addHistLine(); scrollUp(0,1);
945 clearImage(loc(0,0),loc(columns-1,lines-1),' ');
948 /*! fill screen with 'E'
949 This is to aid screen alignment
952 void Screen::helpAlign()
954 clearImage(loc(0,0),loc(columns-1,lines-1),'E');
957 void Screen::clearToEndOfLine()
959 clearImage(loc(cuX,cuY),loc(columns-1,cuY),' ');
962 void Screen::clearToBeginOfLine()
964 clearImage(loc(0,cuY),loc(cuX,cuY),' ');
967 void Screen::clearEntireLine()
969 clearImage(loc(0,cuY),loc(columns-1,cuY),' ');
972 void Screen::setRendition(int re)
974 currentRendition |= re;
975 updateEffectiveRendition();
978 void Screen::resetRendition(int re)
980 currentRendition &= ~re;
981 updateEffectiveRendition();
984 void Screen::setDefaultRendition()
986 setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR);
987 setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR);
988 currentRendition = DEFAULT_RENDITION;
989 updateEffectiveRendition();
992 void Screen::setForeColor(int space, int color)
994 currentForeground = CharacterColor(space, color);
996 if ( currentForeground.isValid() )
997 updateEffectiveRendition();
998 else
999 setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR);
1002 void Screen::setBackColor(int space, int color)
1004 currentBackground = CharacterColor(space, color);
1006 if ( currentBackground.isValid() )
1007 updateEffectiveRendition();
1008 else
1009 setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR);
1012 void Screen::clearSelection()
1014 selBottomRight = -1;
1015 selTopLeft = -1;
1016 selBegin = -1;
1019 void Screen::getSelectionStart(int& column , int& line) const
1021 if ( selTopLeft != -1 )
1023 column = selTopLeft % columns;
1024 line = selTopLeft / columns;
1026 else
1028 column = cuX + getHistLines();
1029 line = cuY + getHistLines();
1032 void Screen::getSelectionEnd(int& column , int& line) const
1034 if ( selBottomRight != -1 )
1036 column = selBottomRight % columns;
1037 line = selBottomRight / columns;
1039 else
1041 column = cuX + getHistLines();
1042 line = cuY + getHistLines();
1045 void Screen::setSelectionStart(const int x, const int y, const bool mode)
1047 selBegin = loc(x,y);
1048 /* FIXME, HACK to correct for x too far to the right... */
1049 if (x == columns) selBegin--;
1051 selBottomRight = selBegin;
1052 selTopLeft = selBegin;
1053 blockSelectionMode = mode;
1056 void Screen::setSelectionEnd( const int x, const int y)
1058 if (selBegin == -1)
1059 return;
1061 int endPos = loc(x,y);
1063 if (endPos < selBegin)
1065 selTopLeft = endPos;
1066 selBottomRight = selBegin;
1068 else
1070 /* FIXME, HACK to correct for x too far to the right... */
1071 if (x == columns)
1072 endPos--;
1074 selTopLeft = selBegin;
1075 selBottomRight = endPos;
1078 // Normalize the selection in column mode
1079 if (blockSelectionMode)
1081 int topRow = selTopLeft / columns;
1082 int topColumn = selTopLeft % columns;
1083 int bottomRow = selBottomRight / columns;
1084 int bottomColumn = selBottomRight % columns;
1086 selTopLeft = loc(qMin(topColumn,bottomColumn),topRow);
1087 selBottomRight = loc(qMax(topColumn,bottomColumn),bottomRow);
1091 bool Screen::isSelected( const int x,const int y) const
1093 bool columnInSelection = true;
1094 if (blockSelectionMode)
1096 columnInSelection = x >= (selTopLeft % columns) &&
1097 x <= (selBottomRight % columns);
1100 int pos = loc(x,y);
1101 return pos >= selTopLeft && pos <= selBottomRight && columnInSelection;
1104 QString Screen::selectedText(bool preserveLineBreaks) const
1106 QString result;
1107 QTextStream stream(&result, QIODevice::ReadWrite);
1109 PlainTextDecoder decoder;
1110 decoder.begin(&stream);
1111 writeSelectionToStream(&decoder , preserveLineBreaks);
1112 decoder.end();
1114 return result;
1117 bool Screen::isSelectionValid() const
1119 return selTopLeft >= 0 && selBottomRight >= 0;
1122 void Screen::writeSelectionToStream(TerminalCharacterDecoder* decoder ,
1123 bool preserveLineBreaks) const
1125 if (!isSelectionValid())
1126 return;
1127 writeToStream(decoder,selTopLeft,selBottomRight,preserveLineBreaks);
1130 void Screen::writeToStream(TerminalCharacterDecoder* decoder,
1131 int startIndex, int endIndex,
1132 bool preserveLineBreaks) const
1134 int top = startIndex / columns;
1135 int left = startIndex % columns;
1137 int bottom = endIndex / columns;
1138 int right = endIndex % columns;
1140 Q_ASSERT( top >= 0 && left >= 0 && bottom >= 0 && right >= 0 );
1142 for (int y=top;y<=bottom;y++)
1144 int start = 0;
1145 if ( y == top || blockSelectionMode ) start = left;
1147 int count = -1;
1148 if ( y == bottom || blockSelectionMode ) count = right - start + 1;
1150 const bool appendNewLine = ( y != bottom );
1151 int copied = copyLineToStream( y,
1152 start,
1153 count,
1154 decoder,
1155 appendNewLine,
1156 preserveLineBreaks );
1158 // if the selection goes beyond the end of the last line then
1159 // append a new line character.
1161 // this makes it possible to 'select' a trailing new line character after
1162 // the text on a line.
1163 if ( y == bottom &&
1164 copied < count )
1166 Character newLineChar('\n');
1167 decoder->decodeLine(&newLineChar,1,0);
1172 int Screen::copyLineToStream(int line ,
1173 int start,
1174 int count,
1175 TerminalCharacterDecoder* decoder,
1176 bool appendNewLine,
1177 bool preserveLineBreaks) const
1179 //buffer to hold characters for decoding
1180 //the buffer is static to avoid initialising every
1181 //element on each call to copyLineToStream
1182 //(which is unnecessary since all elements will be overwritten anyway)
1183 static const int MAX_CHARS = 1024;
1184 static Character characterBuffer[MAX_CHARS];
1186 assert( count < MAX_CHARS );
1188 LineProperty currentLineProperties = 0;
1190 //determine if the line is in the history buffer or the screen image
1191 if (line < history->getLines())
1193 const int lineLength = history->getLineLen(line);
1195 // ensure that start position is before end of line
1196 start = qMin(start,qMax(0,lineLength-1));
1198 // retrieve line from history buffer. It is assumed
1199 // that the history buffer does not store trailing white space
1200 // at the end of the line, so it does not need to be trimmed here
1201 if (count == -1)
1203 count = lineLength-start;
1205 else
1207 count = qMin(start+count,lineLength)-start;
1210 // safety checks
1211 assert( start >= 0 );
1212 assert( count >= 0 );
1213 assert( (start+count) <= history->getLineLen(line) );
1215 history->getCells(line,start,count,characterBuffer);
1217 if ( history->isWrappedLine(line) )
1218 currentLineProperties |= LINE_WRAPPED;
1220 else
1222 if ( count == -1 )
1223 count = columns - start;
1225 assert( count >= 0 );
1227 const int screenLine = line-history->getLines();
1229 Character* data = screenLines[screenLine].data();
1230 int length = screenLines[screenLine].count();
1232 // ignore trailing white space at the end of the line
1233 for (int i = length-1; i >= 0; i--)
1235 if (data[i].character == ' ')
1236 length--;
1237 else
1238 break;
1240 //retrieve line from screen image
1241 for (int i=start;i < qMin(start+count,length);i++)
1243 characterBuffer[i-start] = data[i];
1246 // count cannot be any greater than length
1247 count = qBound(0,count,length-start);
1249 Q_ASSERT( screenLine < lineProperties.count() );
1250 currentLineProperties |= lineProperties[screenLine];
1253 // add new line character at end
1254 const bool omitLineBreak = (currentLineProperties & LINE_WRAPPED) ||
1255 !preserveLineBreaks;
1257 if ( !omitLineBreak && appendNewLine && (count+1 < MAX_CHARS) )
1259 characterBuffer[count] = '\n';
1260 count++;
1263 //decode line and write to text stream
1264 decoder->decodeLine( (Character*) characterBuffer ,
1265 count, currentLineProperties );
1267 return count;
1270 void Screen::writeLinesToStream(TerminalCharacterDecoder* decoder, int fromLine, int toLine) const
1272 writeToStream(decoder,loc(0,fromLine),loc(columns-1,toLine));
1275 void Screen::addHistLine()
1277 // add line to history buffer
1278 // we have to take care about scrolling, too...
1280 if (hasScroll())
1282 int oldHistLines = history->getLines();
1284 history->addCellsVector(screenLines[0]);
1285 history->addLine( lineProperties[0] & LINE_WRAPPED );
1287 int newHistLines = history->getLines();
1289 bool beginIsTL = (selBegin == selTopLeft);
1291 // If the history is full, increment the count
1292 // of dropped lines
1293 if ( newHistLines == oldHistLines )
1294 _droppedLines++;
1296 // Adjust selection for the new point of reference
1297 if (newHistLines > oldHistLines)
1299 if (selBegin != -1)
1301 selTopLeft += columns;
1302 selBottomRight += columns;
1306 if (selBegin != -1)
1308 // Scroll selection in history up
1309 int top_BR = loc(0, 1+newHistLines);
1311 if (selTopLeft < top_BR)
1312 selTopLeft -= columns;
1314 if (selBottomRight < top_BR)
1315 selBottomRight -= columns;
1317 if (selBottomRight < 0)
1318 clearSelection();
1319 else
1321 if (selTopLeft < 0)
1322 selTopLeft = 0;
1325 if (beginIsTL)
1326 selBegin = selTopLeft;
1327 else
1328 selBegin = selBottomRight;
1334 int Screen::getHistLines() const
1336 return history->getLines();
1339 void Screen::setScroll(const HistoryType& t , bool copyPreviousScroll)
1341 clearSelection();
1343 if ( copyPreviousScroll )
1344 history = t.scroll(history);
1345 else
1347 HistoryScroll* oldScroll = history;
1348 history = t.scroll(0);
1349 delete oldScroll;
1353 bool Screen::hasScroll() const
1355 return history->hasScroll();
1358 const HistoryType& Screen::getScroll() const
1360 return history->getType();
1363 void Screen::setLineProperty(LineProperty property , bool enable)
1365 if ( enable )
1366 lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | property);
1367 else
1368 lineProperties[cuY] = (LineProperty)(lineProperties[cuY] & ~property);
1370 void Screen::fillWithDefaultChar(Character* dest, int count)
1372 for (int i=0;i<count;i++)
1373 dest[i] = defaultChar;