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
35
#include <QTextStream>
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.
61
#define loc(X,Y) ((Y)*columns+(X))
65
Character Screen::defaultChar = Character(' ',
66
CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR),
67
CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR),
70
//#define REVERSE_WRAPPED_LINES // for wrapped line debug
72
Screen::Screen(int l, int c)
75
screenLines(new ImageLine[lines+1] ),
78
history(new HistoryScrollNone()),
81
_topMargin(0), _bottomMargin(0),
82
selBegin(0), selTopLeft(0), selBottomRight(0),
83
blockSelectionMode(false),
84
effectiveForeground(CharacterColor()), effectiveBackground(CharacterColor()), effectiveRendition(0),
87
lineProperties.resize(lines+1);
88
for (int i=0;i<lines+1;i++)
89
lineProperties[i]=LINE_DEFAULT;
101
delete[] screenLines;
105
void Screen::cursorUp(int n)
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)
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)
126
if (n == 0) n = 1; // Default
127
cuX = qMin(columns-1,cuX); // nowrap!
131
void Screen::cursorRight(int n)
134
if (n == 0) n = 1; // Default
135
cuX = qMin(columns-1,cuX+n);
138
void Screen::setMargins(int top, int bot)
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
{ //Debug()<<" setRegion("<<top<<","<<bot<<") : bad range.";
147
return; // Default error action: ignore
152
cuY = getMode(MODE_Origin) ? top : 0;
156
int Screen::topMargin() const
160
int Screen::bottomMargin() const
162
return _bottomMargin;
168
if (cuY == _bottomMargin)
170
else if (cuY < lines-1)
174
void Screen::reverseIndex()
177
if (cuY == _topMargin)
178
scrollDown(_topMargin,1);
183
void Screen::nextLine()
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)
200
// always delete at least one char
204
// if cursor is beyond the end of the line there is nothing to do
205
if ( cuX >= screenLines[cuY].count() )
208
if ( cuX+n > screenLines[cuY].count() )
209
n = screenLines[cuY].count() - cuX;
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
236
void Screen::insertLines(int n)
238
if (n == 0) n = 1; // Default
242
void Screen::setMode(int m)
244
currentModes[m] = true;
247
case MODE_Origin : cuX = 0; cuY = _topMargin; break; //FIXME: home
251
void Screen::resetMode(int m)
253
currentModes[m] = false;
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;
321
delete[] screenLines;
322
screenLines = newScreenLines;
325
columns = new_columns;
326
cuX = qMin(cuX,columns-1);
327
cuY = qMin(cuY,lines-1);
329
// FIXME: try to keep values, evtl.
331
_bottomMargin=lines-1;
336
void Screen::setDefaultMargins()
339
_bottomMargin = lines-1;
344
Clarifying rendition here and in the display.
346
currently, the display's color table is
348
dft_fg, dft_bg, dim 0..7, intensive 0..7
350
currentForeground, currentBackground contain values 0..8;
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
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
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;
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
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 );
466
const int linesInHistoryBuffer = qBound(0,history->getLines()-startLine,mergedLines);
467
const int linesInScreenBuffer = mergedLines - linesInHistoryBuffer;
469
// copy lines from history buffer
470
if (linesInHistoryBuffer > 0)
471
copyFromHistory(dest,startLine,linesInHistoryBuffer);
473
// copy lines from screen buffer
474
if (linesInScreenBuffer > 0)
475
copyFromScreen(dest + linesInHistoryBuffer*columns,
476
startLine + linesInHistoryBuffer - history->getLines(),
477
linesInScreenBuffer);
479
// invert display when in screen mode
480
if (getMode(MODE_Screen))
482
for (int i = 0; i < mergedLines*columns; i++)
483
reverseRendition(dest[i]); // for reverse display
486
// mark the character at the current cursor position
487
int cursorIndex = loc(cuX, cuY + linesInHistoryBuffer);
488
if(getMode(MODE_Cursor) && cursorIndex < columns*mergedLines)
489
dest[cursorIndex].rendition |= RE_CURSOR;
492
QVector<LineProperty> Screen::getLineProperties( int startLine , int endLine ) const
494
Q_ASSERT( startLine >= 0 );
495
Q_ASSERT( endLine >= startLine && endLine < history->getLines() + lines );
497
const int mergedLines = endLine-startLine+1;
498
const int linesInHistory = qBound(0,history->getLines()-startLine,mergedLines);
499
const int linesInScreen = mergedLines - linesInHistory;
501
QVector<LineProperty> result(mergedLines);
504
// copy properties for lines in history
505
for (int line = startLine; line < startLine + linesInHistory; line++)
507
//TODO Support for line properties other than wrapped lines
508
if (history->isWrappedLine(line))
510
result[index] = (LineProperty)(result[index] | LINE_WRAPPED);
515
// copy properties for lines in screen buffer
516
const int firstScreenLine = startLine + linesInHistory - history->getLines();
517
for (int line = firstScreenLine; line < firstScreenLine+linesInScreen; line++)
519
result[index]=lineProperties[line];
526
void Screen::reset(bool clearScreen)
528
setMode(MODE_Wrap ); saveMode(MODE_Wrap ); // wrap at end of margin
529
resetMode(MODE_Origin); saveMode(MODE_Origin); // position refere to [1,1]
530
resetMode(MODE_Insert); saveMode(MODE_Insert); // overstroke
531
setMode(MODE_Cursor); // cursor visible
532
resetMode(MODE_Screen); // screen not inverse
533
resetMode(MODE_NewLine);
536
_bottomMargin=lines-1;
538
setDefaultRendition();
551
void Screen::backspace()
553
cuX = qMin(columns-1,cuX); // nowrap!
556
if (screenLines[cuY].size() < cuX+1)
557
screenLines[cuY].resize(cuX+1);
560
screenLines[cuY][cuX].character = ' ';
563
void Screen::tab(int n)
565
// note that TAB is a format effector (does not write ' ');
567
while((n > 0) && (cuX < columns-1))
570
while((cuX < columns-1) && !tabStops[cuX])
576
void Screen::backtab(int n)
578
// note that TAB is a format effector (does not write ' ');
580
while((n > 0) && (cuX > 0))
582
cursorLeft(1); while((cuX > 0) && !tabStops[cuX]) cursorLeft(1);
587
void Screen::clearTabStops()
589
for (int i = 0; i < columns; i++) tabStops[i] = false;
592
void Screen::changeTabStop(bool set)
594
if (cuX >= columns) return;
598
void Screen::initTabStops()
600
tabStops.resize(columns);
602
// Arrg! The 1st tabstop has to be one longer than the other.
603
// i.e. the kids start counting from 0 instead of 1.
604
// Other programs might behave correctly. Be aware.
605
for (int i = 0; i < columns; i++)
606
tabStops[i] = (i%8 == 0 && i != 0);
609
void Screen::newLine()
611
if (getMode(MODE_NewLine))
616
void Screen::checkSelection(int from, int to)
620
int scr_TL = loc(0, history->getLines());
621
//Clear entire selection if it overlaps region [from, to]
622
if ( (selBottomRight >= (from+scr_TL)) && (selTopLeft <= (to+scr_TL)) )
626
void Screen::displayCharacter(unsigned short c)
628
// Note that VT100 does wrapping BEFORE putting the character.
629
// This has impact on the assumption of valid cursor positions.
630
// We indicate the fact that a newline has to be triggered by
631
// putting the cursor one right to the last column of the screen.
633
int w = konsole_wcwidth(c);
637
if (cuX+w > columns) {
638
if (getMode(MODE_Wrap)) {
639
lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | LINE_WRAPPED);
646
// ensure current line vector has enough elements
647
int size = screenLines[cuY].size();
650
screenLines[cuY].resize(cuX+w);
653
if (getMode(MODE_Insert)) insertChars(w);
655
lastPos = loc(cuX,cuY);
657
// check if selection is still valid.
658
checkSelection(lastPos, lastPos);
660
Character& currentChar = screenLines[cuY][cuX];
662
currentChar.character = c;
663
currentChar.foregroundColor = effectiveForeground;
664
currentChar.backgroundColor = effectiveBackground;
665
currentChar.rendition = effectiveRendition;
668
int newCursorX = cuX + w--;
673
if ( screenLines[cuY].size() < cuX + i + 1 )
674
screenLines[cuY].resize(cuX+i+1);
676
Character& ch = screenLines[cuY][cuX + i];
678
ch.foregroundColor = effectiveForeground;
679
ch.backgroundColor = effectiveBackground;
680
ch.rendition = effectiveRendition;
687
void Screen::compose(const QString& /*compose*/)
689
Q_ASSERT( 0 /*Not implemented yet*/ );
691
/* if (lastPos == -1)
694
QChar c(image[lastPos].character);
696
//compose.compose(); ### FIXME!
697
image[lastPos].character = compose[0].unicode();*/
700
int Screen::scrolledLines() const
702
return _scrolledLines;
704
int Screen::droppedLines() const
706
return _droppedLines;
708
void Screen::resetDroppedLines()
712
void Screen::resetScrolledLines()
717
void Screen::scrollUp(int n)
719
if (n == 0) n = 1; // Default
720
if (_topMargin == 0) addHistLine(); // history.history
721
scrollUp(_topMargin, n);
724
QRect Screen::lastScrolledRegion() const
726
return _lastScrolledRegion;
729
void Screen::scrollUp(int from, int n)
731
if (n <= 0 || from + n > _bottomMargin) return;
734
_lastScrolledRegion = QRect(0,_topMargin,columns-1,(_bottomMargin-_topMargin));
736
//FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds.
737
moveImage(loc(0,from),loc(0,from+n),loc(columns-1,_bottomMargin));
738
clearImage(loc(0,_bottomMargin-n+1),loc(columns-1,_bottomMargin),' ');
741
void Screen::scrollDown(int n)
743
if (n == 0) n = 1; // Default
744
scrollDown(_topMargin, n);
747
void Screen::scrollDown(int from, int n)
751
//FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds.
754
if (from > _bottomMargin)
756
if (from + n > _bottomMargin)
757
n = _bottomMargin - from;
758
moveImage(loc(0,from+n),loc(0,from),loc(columns-1,_bottomMargin-n));
759
clearImage(loc(0,from),loc(columns-1,from+n-1),' ');
762
void Screen::setCursorYX(int y, int x)
764
setCursorY(y); setCursorX(x);
767
void Screen::setCursorX(int x)
769
if (x == 0) x = 1; // Default
771
cuX = qMax(0,qMin(columns-1, x));
774
void Screen::setCursorY(int y)
776
if (y == 0) y = 1; // Default
778
cuY = qMax(0,qMin(lines -1, y + (getMode(MODE_Origin) ? _topMargin : 0) ));
787
void Screen::toStartOfLine()
792
int Screen::getCursorX() const
797
int Screen::getCursorY() const
802
void Screen::clearImage(int loca, int loce, char c)
804
int scr_TL=loc(0,history->getLines());
805
//FIXME: check positions
807
//Clear entire selection if it overlaps region to be moved...
808
if ( (selBottomRight > (loca+scr_TL) )&&(selTopLeft < (loce+scr_TL)) )
813
int topLine = loca/columns;
814
int bottomLine = loce/columns;
816
Character clearCh(c,currentForeground,currentBackground,DEFAULT_RENDITION);
818
//if the character being used to clear the area is the same as the
819
//default character, the affected lines can simply be shrunk.
820
bool isDefaultCh = (clearCh == Character());
822
for (int y=topLine;y<=bottomLine;y++)
824
lineProperties[y] = 0;
826
int endCol = ( y == bottomLine) ? loce%columns : columns-1;
827
int startCol = ( y == topLine ) ? loca%columns : 0;
829
QVector<Character>& line = screenLines[y];
831
if ( isDefaultCh && endCol == columns-1 )
833
line.resize(startCol);
837
if (line.size() < endCol + 1)
838
line.resize(endCol+1);
840
Character* data = line.data();
841
for (int i=startCol;i<=endCol;i++)
847
void Screen::moveImage(int dest, int sourceBegin, int sourceEnd)
849
Q_ASSERT( sourceBegin <= sourceEnd );
851
int lines=(sourceEnd-sourceBegin)/columns;
853
//move screen image and line properties:
854
//the source and destination areas of the image may overlap,
855
//so it matters that we do the copy in the right order -
856
//forwards if dest < sourceBegin or backwards otherwise.
857
//(search the web for 'memmove implementation' for details)
858
if (dest < sourceBegin)
860
for (int i=0;i<=lines;i++)
862
screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ];
863
lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i];
868
for (int i=lines;i>=0;i--)
870
screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ];
871
lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i];
877
int diff = dest - sourceBegin; // Scroll by this amount
879
if ((lastPos < 0) || (lastPos >= (lines*columns)))
883
// Adjust selection to follow scroll.
886
bool beginIsTL = (selBegin == selTopLeft);
887
int diff = dest - sourceBegin; // Scroll by this amount
888
int scr_TL=loc(0,history->getLines());
889
int srca = sourceBegin+scr_TL; // Translate index from screen to global
890
int srce = sourceEnd+scr_TL; // Translate index from screen to global
891
int desta = srca+diff;
892
int deste = srce+diff;
894
if ((selTopLeft >= srca) && (selTopLeft <= srce))
896
else if ((selTopLeft >= desta) && (selTopLeft <= deste))
897
selBottomRight = -1; // Clear selection (see below)
899
if ((selBottomRight >= srca) && (selBottomRight <= srce))
900
selBottomRight += diff;
901
else if ((selBottomRight >= desta) && (selBottomRight <= deste))
902
selBottomRight = -1; // Clear selection (see below)
904
if (selBottomRight < 0)
915
selBegin = selTopLeft;
917
selBegin = selBottomRight;
921
void Screen::clearToEndOfScreen()
923
clearImage(loc(cuX,cuY),loc(columns-1,lines-1),' ');
926
void Screen::clearToBeginOfScreen()
928
clearImage(loc(0,0),loc(cuX,cuY),' ');
931
void Screen::clearEntireScreen()
933
// Add entire screen to history
934
for (int i = 0; i < (lines-1); i++)
936
addHistLine(); scrollUp(0,1);
939
clearImage(loc(0,0),loc(columns-1,lines-1),' ');
942
/*! fill screen with 'E'
943
This is to aid screen alignment
946
void Screen::helpAlign()
948
clearImage(loc(0,0),loc(columns-1,lines-1),'E');
951
void Screen::clearToEndOfLine()
953
clearImage(loc(cuX,cuY),loc(columns-1,cuY),' ');
956
void Screen::clearToBeginOfLine()
958
clearImage(loc(0,cuY),loc(cuX,cuY),' ');
961
void Screen::clearEntireLine()
963
clearImage(loc(0,cuY),loc(columns-1,cuY),' ');
966
void Screen::setRendition(int re)
968
currentRendition |= re;
969
updateEffectiveRendition();
972
void Screen::resetRendition(int re)
974
currentRendition &= ~re;
975
updateEffectiveRendition();
978
void Screen::setDefaultRendition()
980
setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR);
981
setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR);
982
currentRendition = DEFAULT_RENDITION;
983
updateEffectiveRendition();
986
void Screen::setForeColor(int space, int color)
988
currentForeground = CharacterColor(space, color);
990
if ( currentForeground.isValid() )
991
updateEffectiveRendition();
993
setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR);
996
void Screen::setBackColor(int space, int color)
998
currentBackground = CharacterColor(space, color);
1000
if ( currentBackground.isValid() )
1001
updateEffectiveRendition();
1003
setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR);
1006
void Screen::clearSelection()
1008
selBottomRight = -1;
1013
void Screen::getSelectionStart(int& column , int& line) const
1015
if ( selTopLeft != -1 )
1017
column = selTopLeft % columns;
1018
line = selTopLeft / columns;
1022
column = cuX + getHistLines();
1023
line = cuY + getHistLines();
1026
void Screen::getSelectionEnd(int& column , int& line) const
1028
if ( selBottomRight != -1 )
1030
column = selBottomRight % columns;
1031
line = selBottomRight / columns;
1035
column = cuX + getHistLines();
1036
line = cuY + getHistLines();
1039
void Screen::setSelectionStart(const int x, const int y, const bool mode)
1041
selBegin = loc(x,y);
1042
/* FIXME, HACK to correct for x too far to the right... */
1043
if (x == columns) selBegin--;
1045
selBottomRight = selBegin;
1046
selTopLeft = selBegin;
1047
blockSelectionMode = mode;
1050
void Screen::setSelectionEnd( const int x, const int y)
1055
int endPos = loc(x,y);
1057
if (endPos < selBegin)
1059
selTopLeft = endPos;
1060
selBottomRight = selBegin;
1064
/* FIXME, HACK to correct for x too far to the right... */
1068
selTopLeft = selBegin;
1069
selBottomRight = endPos;
1072
// Normalize the selection in column mode
1073
if (blockSelectionMode)
1075
int topRow = selTopLeft / columns;
1076
int topColumn = selTopLeft % columns;
1077
int bottomRow = selBottomRight / columns;
1078
int bottomColumn = selBottomRight % columns;
1080
selTopLeft = loc(qMin(topColumn,bottomColumn),topRow);
1081
selBottomRight = loc(qMax(topColumn,bottomColumn),bottomRow);
1085
bool Screen::isSelected( const int x,const int y) const
1087
bool columnInSelection = true;
1088
if (blockSelectionMode)
1090
columnInSelection = x >= (selTopLeft % columns) &&
1091
x <= (selBottomRight % columns);
1095
return pos >= selTopLeft && pos <= selBottomRight && columnInSelection;
1098
QString Screen::selectedText(bool preserveLineBreaks) const
1101
QTextStream stream(&result, QIODevice::ReadWrite);
1103
PlainTextDecoder decoder;
1104
decoder.begin(&stream);
1105
writeSelectionToStream(&decoder , preserveLineBreaks);
1111
bool Screen::isSelectionValid() const
1113
return selTopLeft >= 0 && selBottomRight >= 0;
1116
void Screen::writeSelectionToStream(TerminalCharacterDecoder* decoder ,
1117
bool preserveLineBreaks) const
1119
if (!isSelectionValid())
1121
writeToStream(decoder,selTopLeft,selBottomRight,preserveLineBreaks);
1124
void Screen::writeToStream(TerminalCharacterDecoder* decoder,
1125
int startIndex, int endIndex,
1126
bool preserveLineBreaks) const
1128
int top = startIndex / columns;
1129
int left = startIndex % columns;
1131
int bottom = endIndex / columns;
1132
int right = endIndex % columns;
1134
Q_ASSERT( top >= 0 && left >= 0 && bottom >= 0 && right >= 0 );
1136
for (int y=top;y<=bottom;y++)
1139
if ( y == top || blockSelectionMode ) start = left;
1142
if ( y == bottom || blockSelectionMode ) count = right - start + 1;
1144
const bool appendNewLine = ( y != bottom );
1145
int copied = copyLineToStream( y,
1150
preserveLineBreaks );
1152
// if the selection goes beyond the end of the last line then
1153
// append a new line character.
1155
// this makes it possible to 'select' a trailing new line character after
1156
// the text on a line.
1160
Character newLineChar('\n');
1161
decoder->decodeLine(&newLineChar,1,0);
1166
int Screen::copyLineToStream(int line ,
1169
TerminalCharacterDecoder* decoder,
1171
bool preserveLineBreaks) const
1173
//buffer to hold characters for decoding
1174
//the buffer is static to avoid initialising every
1175
//element on each call to copyLineToStream
1176
//(which is unnecessary since all elements will be overwritten anyway)
1177
static const int MAX_CHARS = 1024;
1178
static Character characterBuffer[MAX_CHARS];
1180
assert( count < MAX_CHARS );
1182
LineProperty currentLineProperties = 0;
1184
//determine if the line is in the history buffer or the screen image
1185
if (line < history->getLines())
1187
const int lineLength = history->getLineLen(line);
1189
// ensure that start position is before end of line
1190
start = qMin(start,qMax(0,lineLength-1));
1192
// retrieve line from history buffer. It is assumed
1193
// that the history buffer does not store trailing white space
1194
// at the end of the line, so it does not need to be trimmed here
1197
count = lineLength-start;
1201
count = qMin(start+count,lineLength)-start;
1205
assert( start >= 0 );
1206
assert( count >= 0 );
1207
assert( (start+count) <= history->getLineLen(line) );
1209
history->getCells(line,start,count,characterBuffer);
1211
if ( history->isWrappedLine(line) )
1212
currentLineProperties |= LINE_WRAPPED;
1217
count = columns - start;
1219
assert( count >= 0 );
1221
const int screenLine = line-history->getLines();
1223
Character* data = screenLines[screenLine].data();
1224
int length = screenLines[screenLine].count();
1226
//retrieve line from screen image
1227
for (int i=start;i < qMin(start+count,length);i++)
1229
characterBuffer[i-start] = data[i];
1232
// count cannot be any greater than length
1233
count = qBound(0,count,length-start);
1235
Q_ASSERT( screenLine < lineProperties.count() );
1236
currentLineProperties |= lineProperties[screenLine];
1239
// add new line character at end
1240
const bool omitLineBreak = (currentLineProperties & LINE_WRAPPED) ||
1241
!preserveLineBreaks;
1243
if ( !omitLineBreak && appendNewLine && (count+1 < MAX_CHARS) )
1245
characterBuffer[count] = '\n';
1249
//decode line and write to text stream
1250
decoder->decodeLine( (Character*) characterBuffer ,
1251
count, currentLineProperties );
1256
void Screen::writeLinesToStream(TerminalCharacterDecoder* decoder, int fromLine, int toLine) const
1258
writeToStream(decoder,loc(0,fromLine),loc(columns-1,toLine));
1261
void Screen::addHistLine()
1263
// add line to history buffer
1264
// we have to take care about scrolling, too...
1268
int oldHistLines = history->getLines();
1270
history->addCellsVector(screenLines[0]);
1271
history->addLine( lineProperties[0] & LINE_WRAPPED );
1273
int newHistLines = history->getLines();
1275
bool beginIsTL = (selBegin == selTopLeft);
1277
// If the history is full, increment the count
1279
if ( newHistLines == oldHistLines )
1282
// Adjust selection for the new point of reference
1283
if (newHistLines > oldHistLines)
1287
selTopLeft += columns;
1288
selBottomRight += columns;
1294
// Scroll selection in history up
1295
int top_BR = loc(0, 1+newHistLines);
1297
if (selTopLeft < top_BR)
1298
selTopLeft -= columns;
1300
if (selBottomRight < top_BR)
1301
selBottomRight -= columns;
1303
if (selBottomRight < 0)
1312
selBegin = selTopLeft;
1314
selBegin = selBottomRight;
1320
int Screen::getHistLines() const
1322
return history->getLines();
1325
void Screen::setScroll(const HistoryType& t , bool copyPreviousScroll)
1329
if ( copyPreviousScroll )
1330
history = t.scroll(history);
1333
HistoryScroll* oldScroll = history;
1334
history = t.scroll(0);
1339
bool Screen::hasScroll() const
1341
return history->hasScroll();
1344
const HistoryType& Screen::getScroll() const
1346
return history->getType();
1349
void Screen::setLineProperty(LineProperty property , bool enable)
1352
lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | property);
1354
lineProperties[cuY] = (LineProperty)(lineProperties[cuY] & ~property);
1356
void Screen::fillWithDefaultChar(Character* dest, int count)
1358
for (int i=0;i<count;i++)
1359
dest[i] = defaultChar;