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 <QtCore/QTextStream>
36 #include <QtCore/QDate>
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 { kDebug()<<" 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() - 1 - 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
);
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
);
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
);
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
];
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
);
535 _bottomMargin
=lines
-1;
537 setDefaultRendition();
550 void Screen::backspace()
552 cuX
= qMin(columns
-1,cuX
); // nowrap!
555 if (screenLines
[cuY
].size() < cuX
+1)
556 screenLines
[cuY
].resize(cuX
+1);
559 screenLines
[cuY
][cuX
].character
= ' ';
562 void Screen::tab(int n
)
564 // note that TAB is a format effector (does not write ' ');
566 while((n
> 0) && (cuX
< columns
-1))
569 while((cuX
< columns
-1) && !tabStops
[cuX
])
575 void Screen::backtab(int n
)
577 // note that TAB is a format effector (does not write ' ');
579 while((n
> 0) && (cuX
> 0))
581 cursorLeft(1); while((cuX
> 0) && !tabStops
[cuX
]) cursorLeft(1);
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;
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
))
615 void Screen::checkSelection(int from
, int to
)
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
)) )
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
);
636 if (cuX
+w
> columns
) {
637 if (getMode(MODE_Wrap
)) {
638 lineProperties
[cuY
] = (LineProperty
)(lineProperties
[cuY
] | LINE_WRAPPED
);
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
) );
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
;
674 int newCursorX
= cuX
+ w
--;
679 if ( screenLines
[cuY
].size() < cuX
+ i
+ 1 )
680 screenLines
[cuY
].resize(cuX
+i
+1);
682 Character
& ch
= screenLines
[cuY
][cuX
+ i
];
684 ch
.foregroundColor
= effectiveForeground
;
685 ch
.backgroundColor
= effectiveBackground
;
686 ch
.rendition
= effectiveRendition
;
693 void Screen::compose(const QString
& /*compose*/)
695 Q_ASSERT( 0 /*Not implemented yet*/ );
697 /* if (lastPos == -1)
700 QChar c(image[lastPos].character);
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()
718 void Screen::resetScrolledLines()
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;
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
)
757 //FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds.
760 if (from
> _bottomMargin
)
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
777 cuX
= qMax(0,qMin(columns
-1, x
));
780 void Screen::setCursorY(int y
)
782 if (y
== 0) y
= 1; // Default
784 cuY
= qMax(0,qMin(lines
-1, y
+ (getMode(MODE_Origin
) ? _topMargin
: 0) ));
793 void Screen::toStartOfLine()
798 int Screen::getCursorX() const
803 int Screen::getCursorY() const
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
)) )
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
);
843 if (line
.size() < endCol
+ 1)
844 line
.resize(endCol
+1);
846 Character
* data
= line
.data();
847 for (int i
=startCol
;i
<=endCol
;i
++)
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
];
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
];
883 int diff
= dest
- sourceBegin
; // Scroll by this amount
885 if ((lastPos
< 0) || (lastPos
>= (lines
*columns
)))
889 // Adjust selection to follow scroll.
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
))
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)
921 selBegin
= selTopLeft
;
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();
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();
1009 setBackColor(COLOR_SPACE_DEFAULT
,DEFAULT_BACK_COLOR
);
1012 void Screen::clearSelection()
1014 selBottomRight
= -1;
1019 void Screen::getSelectionStart(int& column
, int& line
) const
1021 if ( selTopLeft
!= -1 )
1023 column
= selTopLeft
% columns
;
1024 line
= selTopLeft
/ columns
;
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
;
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
)
1061 int endPos
= loc(x
,y
);
1063 if (endPos
< selBegin
)
1065 selTopLeft
= endPos
;
1066 selBottomRight
= selBegin
;
1070 /* FIXME, HACK to correct for x too far to the right... */
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
);
1101 return pos
>= selTopLeft
&& pos
<= selBottomRight
&& columnInSelection
;
1104 QString
Screen::selectedText(bool preserveLineBreaks
) const
1107 QTextStream
stream(&result
, QIODevice::ReadWrite
);
1109 PlainTextDecoder decoder
;
1110 decoder
.begin(&stream
);
1111 writeSelectionToStream(&decoder
, preserveLineBreaks
);
1117 bool Screen::isSelectionValid() const
1119 return selTopLeft
>= 0 && selBottomRight
>= 0;
1122 void Screen::writeSelectionToStream(TerminalCharacterDecoder
* decoder
,
1123 bool preserveLineBreaks
) const
1125 if (!isSelectionValid())
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
++)
1145 if ( y
== top
|| blockSelectionMode
) start
= left
;
1148 if ( y
== bottom
|| blockSelectionMode
) count
= right
- start
+ 1;
1150 const bool appendNewLine
= ( y
!= bottom
);
1151 int copied
= copyLineToStream( y
,
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.
1166 Character
newLineChar('\n');
1167 decoder
->decodeLine(&newLineChar
,1,0);
1172 int Screen::copyLineToStream(int line
,
1175 TerminalCharacterDecoder
* decoder
,
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
1203 count
= lineLength
-start
;
1207 count
= qMin(start
+count
,lineLength
)-start
;
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
;
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
== ' ')
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';
1263 //decode line and write to text stream
1264 decoder
->decodeLine( (Character
*) characterBuffer
,
1265 count
, currentLineProperties
);
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...
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
1293 if ( newHistLines
== oldHistLines
)
1296 // Adjust selection for the new point of reference
1297 if (newHistLines
> oldHistLines
)
1301 selTopLeft
+= columns
;
1302 selBottomRight
+= columns
;
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)
1326 selBegin
= selTopLeft
;
1328 selBegin
= selBottomRight
;
1334 int Screen::getHistLines() const
1336 return history
->getLines();
1339 void Screen::setScroll(const HistoryType
& t
, bool copyPreviousScroll
)
1343 if ( copyPreviousScroll
)
1344 history
= t
.scroll(history
);
1347 HistoryScroll
* oldScroll
= history
;
1348 history
= t
.scroll(0);
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
)
1366 lineProperties
[cuY
] = (LineProperty
)(lineProperties
[cuY
] | property
);
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
;