1 /*
2 This file is part of Konsole, an X terminal.
3
4 Copyright 2007-2008 by Robert Knight <robert.knight@gmail.com>
5 Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
6
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.
11
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.
16
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.
21 */
22
23 // Own
24 #include "Screen.h"
25
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>
33
34 // Qt
35 #include <QTextStream>
36 #include <QDate>
37
38 // KDE
39 //#include <kdebug.h>
40
41 // Konsole
42 #include "konsole_wcwidth.h"
43 #include "TerminalCharacterDecoder.h"
44
45 using namespace Konsole;
46
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
50
51 //Macro to convert x,y position on screen to position within an image.
52 //
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
63
64
65 Character Screen::defaultChar = Character(' ',
66 CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR),
67 CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR),
68 DEFAULT_RENDITION);
69
70 //#define REVERSE_WRAPPED_LINES // for wrapped line debug
71
Screen(int l,int c)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)
86 {
87 lineProperties.resize(lines+1);
88 for (int i=0;i<lines+1;i++)
89 lineProperties[i]=LINE_DEFAULT;
90
91 initTabStops();
92 clearSelection();
93 reset();
94 }
95
96 /*! Destructor
97 */
98
~Screen()99 Screen::~Screen()
100 {
101 delete[] screenLines;
102 delete history;
103 }
104
cursorUp(int n)105 void Screen::cursorUp(int n)
106 //=CUU
107 {
108 if (n == 0) n = 1; // Default
109 int stop = cuY < _topMargin ? 0 : _topMargin;
110 cuX = std::min(columns-1,cuX); // nowrap!
111 cuY = std::max(stop,cuY-n);
112 }
113
cursorDown(int n)114 void Screen::cursorDown(int n)
115 //=CUD
116 {
117 if (n == 0) n = 1; // Default
118 int stop = cuY > _bottomMargin ? lines-1 : _bottomMargin;
119 cuX = std::min(columns-1,cuX); // nowrap!
120 cuY = std::min(stop,cuY+n);
121 }
122
cursorLeft(int n)123 void Screen::cursorLeft(int n)
124 //=CUB
125 {
126 if (n == 0) n = 1; // Default
127 cuX = std::min(columns-1,cuX); // nowrap!
128 cuX = std::max(0,cuX-n);
129 }
130
cursorRight(int n)131 void Screen::cursorRight(int n)
132 //=CUF
133 {
134 if (n == 0) n = 1; // Default
135 cuX = std::min(columns-1,cuX+n);
136 }
137
setMargins(int top,int bot)138 void Screen::setMargins(int top, int bot)
139 //=STBM
140 {
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
148 }
149 _topMargin = top;
150 _bottomMargin = bot;
151 cuX = 0;
152 cuY = getMode(MODE_Origin) ? top : 0;
153
154 }
155
topMargin() const156 int Screen::topMargin() const
157 {
158 return _topMargin;
159 }
bottomMargin() const160 int Screen::bottomMargin() const
161 {
162 return _bottomMargin;
163 }
164
index()165 void Screen::index()
166 //=IND
167 {
168 if (cuY == _bottomMargin)
169 scrollUp(1);
170 else if (cuY < lines-1)
171 cuY += 1;
172 }
173
reverseIndex()174 void Screen::reverseIndex()
175 //=RI
176 {
177 if (cuY == _topMargin)
178 scrollDown(_topMargin,1);
179 else if (cuY > 0)
180 cuY -= 1;
181 }
182
nextLine()183 void Screen::nextLine()
184 //=NEL
185 {
186 toStartOfLine(); index();
187 }
188
eraseChars(int n)189 void Screen::eraseChars(int n)
190 {
191 if (n == 0) n = 1; // Default
192 int p = std::max(0,std::min(cuX+n-1,columns-1));
193 clearImage(loc(cuX,cuY),loc(p,cuY),' ');
194 }
195
deleteChars(int n)196 void Screen::deleteChars(int n)
197 {
198 Q_ASSERT( n >= 0 );
199
200 // always delete at least one char
201 if (n == 0)
202 n = 1;
203
204 // if cursor is beyond the end of the line there is nothing to do
205 if ( cuX >= screenLines[cuY].count() )
206 return;
207
208 if ( cuX+n > screenLines[cuY].count() )
209 n = screenLines[cuY].count() - cuX;
210
211 Q_ASSERT( n >= 0 );
212 Q_ASSERT( cuX+n <= screenLines[cuY].count() );
213
214 screenLines[cuY].remove(cuX,n);
215 }
216
insertChars(int n)217 void Screen::insertChars(int n)
218 {
219 if (n == 0) n = 1; // Default
220
221 if ( screenLines[cuY].size() < cuX )
222 screenLines[cuY].resize(cuX);
223
224 screenLines[cuY].insert(cuX,n,' ');
225
226 if ( screenLines[cuY].count() > columns )
227 screenLines[cuY].resize(columns);
228 }
229
deleteLines(int n)230 void Screen::deleteLines(int n)
231 {
232 if (n == 0) n = 1; // Default
233 scrollUp(cuY,n);
234 }
235
insertLines(int n)236 void Screen::insertLines(int n)
237 {
238 if (n == 0) n = 1; // Default
239 scrollDown(cuY,n);
240 }
241
setMode(int m)242 void Screen::setMode(int m)
243 {
244 currentModes[m] = true;
245 switch(m)
246 {
247 case MODE_Origin : cuX = 0; cuY = _topMargin; break; //FIXME: home
248 }
249 }
250
resetMode(int m)251 void Screen::resetMode(int m)
252 {
253 currentModes[m] = false;
254 switch(m)
255 {
256 case MODE_Origin : cuX = 0; cuY = 0; break; //FIXME: home
257 }
258 }
259
saveMode(int m)260 void Screen::saveMode(int m)
261 {
262 savedModes[m] = currentModes[m];
263 }
264
restoreMode(int m)265 void Screen::restoreMode(int m)
266 {
267 currentModes[m] = savedModes[m];
268 }
269
getMode(int m) const270 bool Screen::getMode(int m) const
271 {
272 return currentModes[m];
273 }
274
saveCursor()275 void Screen::saveCursor()
276 {
277 savedState.cursorColumn = cuX;
278 savedState.cursorLine = cuY;
279 savedState.rendition = currentRendition;
280 savedState.foreground = currentForeground;
281 savedState.background = currentBackground;
282 }
283
restoreCursor()284 void Screen::restoreCursor()
285 {
286 cuX = std::min(savedState.cursorColumn,columns-1);
287 cuY = std::min(savedState.cursorLine,lines-1);
288 currentRendition = savedState.rendition;
289 currentForeground = savedState.foreground;
290 currentBackground = savedState.background;
291 updateEffectiveRendition();
292 }
293
resizeImage(int new_lines,int new_columns)294 void Screen::resizeImage(int new_lines, int new_columns)
295 {
296 if ((new_lines==lines) && (new_columns==columns)) return;
297
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++)
302 {
303 addHistLine(); scrollUp(0,1);
304 }
305 }
306
307 // create new screen lines and copy from old to new
308
309 ImageLine* newScreenLines = new ImageLine[new_lines+1];
310 for (int i=0; i < std::min(lines,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 );
314
315 lineProperties.resize(new_lines+1);
316 for (int i=lines;(i > 0) && (i<new_lines+1);i++)
317 lineProperties[i] = LINE_DEFAULT;
318
319 clearSelection();
320
321 delete[] screenLines;
322 screenLines = newScreenLines;
323
324 lines = new_lines;
325 columns = new_columns;
326 cuX = std::min(cuX,columns-1);
327 cuY = std::min(cuY,lines-1);
328
329 // FIXME: try to keep values, evtl.
330 _topMargin=0;
331 _bottomMargin=lines-1;
332 initTabStops();
333 clearSelection();
334 }
335
setDefaultMargins()336 void Screen::setDefaultMargins()
337 {
338 _topMargin = 0;
339 _bottomMargin = lines-1;
340 }
341
342
343 /*
344 Clarifying rendition here and in the display.
345
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
349
350 currentForeground, currentBackground contain values 0..8;
351 - 0 = default color
352 - 1..8 = ansi specified color
353
354 re_fg, re_bg contain values 0..17
355 due to the TerminalDisplay's color table
356
357 rendition attributes are
358
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
367
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.
375 */
376
reverseRendition(Character & p) const377 void Screen::reverseRendition(Character& p) const
378 {
379 CharacterColor f = p.foregroundColor;
380 CharacterColor b = p.backgroundColor;
381
382 p.foregroundColor = b;
383 p.backgroundColor = f; //p->r &= ~RE_TRANSPARENT;
384 }
385
updateEffectiveRendition()386 void Screen::updateEffectiveRendition()
387 {
388 effectiveRendition = currentRendition;
389 if (currentRendition & RE_REVERSE)
390 {
391 effectiveForeground = currentBackground;
392 effectiveBackground = currentForeground;
393 }
394 else
395 {
396 effectiveForeground = currentForeground;
397 effectiveBackground = currentBackground;
398 }
399
400 if (currentRendition & RE_BOLD)
401 effectiveForeground.toggleIntensive();
402 }
403
copyFromHistory(Character * dest,int startLine,int count) const404 void Screen::copyFromHistory(Character* dest, int startLine, int count) const
405 {
406 // cppcheck-suppress assertWithSideEffect
407 Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= history->getLines() );
408
409 for (int line = startLine; line < startLine + count; line++)
410 {
411 const int length = std::min(columns,history->getLineLen(line));
412 const int destLineOffset = (line-startLine)*columns;
413
414 history->getCells(line,0,length,dest + destLineOffset);
415
416 for (int column = length; column < columns; column++)
417 dest[destLineOffset+column] = defaultChar;
418
419 // invert selected text
420 if (selBegin !=-1)
421 {
422 for (int column = 0; column < columns; column++)
423 {
424 if (isSelected(column,line))
425 {
426 reverseRendition(dest[destLineOffset + column]);
427 }
428 }
429 }
430 }
431 }
432
copyFromScreen(Character * dest,int startLine,int count) const433 void Screen::copyFromScreen(Character* dest , int startLine , int count) const
434 {
435 Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= lines );
436
437 for (int line = startLine; line < (startLine+count) ; line++)
438 {
439 int srcLineStartIndex = line*columns;
440 int destLineStartIndex = (line-startLine)*columns;
441
442 for (int column = 0; column < columns; column++)
443 {
444 int srcIndex = srcLineStartIndex + column;
445 int destIndex = destLineStartIndex + column;
446
447 dest[destIndex] = screenLines[srcIndex/columns].value(srcIndex%columns,defaultChar);
448
449 // invert selected text
450 if (selBegin != -1 && isSelected(column,line + history->getLines()))
451 reverseRendition(dest[destIndex]);
452 }
453
454 }
455 }
456
getImage(Character * dest,int size,int startLine,int endLine) const457 void Screen::getImage( Character* dest, int size, int startLine, int endLine ) const
458 {
459 Q_ASSERT( startLine >= 0 );
460 // cppcheck-suppress assertWithSideEffect
461 Q_ASSERT( endLine >= startLine && endLine < history->getLines() + lines );
462
463 const int mergedLines = endLine - startLine + 1;
464
465 Q_ASSERT( size >= mergedLines * columns );
466 Q_UNUSED( size )
467
468 const int linesInHistoryBuffer = qBound(0,history->getLines()-startLine,mergedLines);
469 const int linesInScreenBuffer = mergedLines - linesInHistoryBuffer;
470
471 // copy lines from history buffer
472 if (linesInHistoryBuffer > 0)
473 copyFromHistory(dest,startLine,linesInHistoryBuffer);
474
475 // copy lines from screen buffer
476 if (linesInScreenBuffer > 0)
477 copyFromScreen(dest + linesInHistoryBuffer*columns,
478 startLine + linesInHistoryBuffer - history->getLines(),
479 linesInScreenBuffer);
480
481 // invert display when in screen mode
482 if (getMode(MODE_Screen))
483 {
484 for (int i = 0; i < mergedLines*columns; i++)
485 reverseRendition(dest[i]); // for reverse display
486 }
487
488 // mark the character at the current cursor position
489 int cursorIndex = loc(cuX, cuY + linesInHistoryBuffer);
490 if(getMode(MODE_Cursor) && cursorIndex < columns*mergedLines)
491 dest[cursorIndex].rendition |= RE_CURSOR;
492 }
493
getLineProperties(int startLine,int endLine) const494 QVector<LineProperty> Screen::getLineProperties( int startLine , int endLine ) const
495 {
496 Q_ASSERT( startLine >= 0 );
497 // cppcheck-suppress assertWithSideEffect
498 Q_ASSERT( endLine >= startLine && endLine < history->getLines() + lines );
499
500 const int mergedLines = endLine-startLine+1;
501 const int linesInHistory = qBound(0,history->getLines()-startLine,mergedLines);
502 const int linesInScreen = mergedLines - linesInHistory;
503
504 QVector<LineProperty> result(mergedLines);
505 int index = 0;
506
507 // copy properties for lines in history
508 for (int line = startLine; line < startLine + linesInHistory; line++)
509 {
510 //TODO Support for line properties other than wrapped lines
511 if (history->isWrappedLine(line))
512 {
513 result[index] = (LineProperty)(result[index] | LINE_WRAPPED);
514 }
515 index++;
516 }
517
518 // copy properties for lines in screen buffer
519 const int firstScreenLine = startLine + linesInHistory - history->getLines();
520 for (int line = firstScreenLine; line < firstScreenLine+linesInScreen; line++)
521 {
522 result[index]=lineProperties[line];
523 index++;
524 }
525
526 return result;
527 }
528
reset(bool clearScreen)529 void Screen::reset(bool clearScreen)
530 {
531 setMode(MODE_Wrap ); saveMode(MODE_Wrap ); // wrap at end of margin
532 resetMode(MODE_Origin); saveMode(MODE_Origin); // position refers to [1,1]
533 resetMode(MODE_Insert); saveMode(MODE_Insert); // overstroke
534 setMode(MODE_Cursor); // cursor visible
535 resetMode(MODE_Screen); // screen not inverse
536 resetMode(MODE_NewLine);
537
538 _topMargin=0;
539 _bottomMargin=lines-1;
540
541 setDefaultRendition();
542 saveCursor();
543
544 if ( clearScreen )
545 clear();
546 }
547
clear()548 void Screen::clear()
549 {
550 clearEntireScreen();
551 home();
552 }
553
backspace()554 void Screen::backspace()
555 {
556 cuX = std::min(columns-1,cuX); // nowrap!
557 cuX = std::max(0,cuX-1);
558
559 if (screenLines[cuY].size() < cuX+1)
560 screenLines[cuY].resize(cuX+1);
561
562 if (BS_CLEARS)
563 screenLines[cuY][cuX].character = ' ';
564 }
565
tab(int n)566 void Screen::tab(int n)
567 {
568 // note that TAB is a format effector (does not write ' ');
569 if (n == 0) n = 1;
570 while((n > 0) && (cuX < columns-1))
571 {
572 cursorRight(1);
573 while((cuX < columns-1) && !tabStops[cuX])
574 cursorRight(1);
575 n--;
576 }
577 }
578
backtab(int n)579 void Screen::backtab(int n)
580 {
581 // note that TAB is a format effector (does not write ' ');
582 if (n == 0) n = 1;
583 while((n > 0) && (cuX > 0))
584 {
585 cursorLeft(1); while((cuX > 0) && !tabStops[cuX]) cursorLeft(1);
586 n--;
587 }
588 }
589
clearTabStops()590 void Screen::clearTabStops()
591 {
592 for (int i = 0; i < columns; i++) tabStops[i] = false;
593 }
594
changeTabStop(bool set)595 void Screen::changeTabStop(bool set)
596 {
597 if (cuX >= columns) return;
598 tabStops[cuX] = set;
599 }
600
initTabStops()601 void Screen::initTabStops()
602 {
603 tabStops.resize(columns);
604
605 // Arrg! The 1st tabstop has to be one longer than the other.
606 // i.e. the kids start counting from 0 instead of 1.
607 // Other programs might behave correctly. Be aware.
608 for (int i = 0; i < columns; i++)
609 tabStops[i] = (i%8 == 0 && i != 0);
610 }
611
newLine()612 void Screen::newLine()
613 {
614 if (getMode(MODE_NewLine))
615 toStartOfLine();
616 index();
617 }
618
checkSelection(int from,int to)619 void Screen::checkSelection(int from, int to)
620 {
621 if (selBegin == -1)
622 return;
623 int scr_TL = loc(0, history->getLines());
624 //Clear entire selection if it overlaps region [from, to]
625 if ( (selBottomRight >= (from+scr_TL)) && (selTopLeft <= (to+scr_TL)) )
626 clearSelection();
627 }
628
displayCharacter(unsigned short c)629 void Screen::displayCharacter(unsigned short c)
630 {
631 // Note that VT100 does wrapping BEFORE putting the character.
632 // This has impact on the assumption of valid cursor positions.
633 // We indicate the fact that a newline has to be triggered by
634 // putting the cursor one right to the last column of the screen.
635
636 int w = konsole_wcwidth(c);
637 if (w <= 0)
638 return;
639
640 if (cuX+w > columns) {
641 if (getMode(MODE_Wrap)) {
642 lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | LINE_WRAPPED);
643 nextLine();
644 }
645 else
646 cuX = columns-w;
647 }
648
649 // ensure current line vector has enough elements
650 int size = screenLines[cuY].size();
651 if (size < cuX+w)
652 {
653 screenLines[cuY].resize(cuX+w);
654 }
655
656 if (getMode(MODE_Insert)) insertChars(w);
657
658 lastPos = loc(cuX,cuY);
659
660 // check if selection is still valid.
661 checkSelection(lastPos, lastPos);
662
663 Character& currentChar = screenLines[cuY][cuX];
664
665 currentChar.character = c;
666 currentChar.foregroundColor = effectiveForeground;
667 currentChar.backgroundColor = effectiveBackground;
668 currentChar.rendition = effectiveRendition;
669
670 int i = 0;
671 int newCursorX = cuX + w--;
672 while(w)
673 {
674 i++;
675
676 if ( screenLines[cuY].size() < cuX + i + 1 )
677 screenLines[cuY].resize(cuX+i+1);
678
679 Character& ch = screenLines[cuY][cuX + i];
680 ch.character = 0;
681 ch.foregroundColor = effectiveForeground;
682 ch.backgroundColor = effectiveBackground;
683 ch.rendition = effectiveRendition;
684
685 w--;
686 }
687 cuX = newCursorX;
688 }
689
compose(const QString &)690 void Screen::compose(const QString& /*compose*/)
691 {
692 Q_ASSERT( 0 /*Not implemented yet*/ );
693
694 /* if (lastPos == -1)
695 return;
696
697 QChar c(image[lastPos].character);
698 compose.prepend(c);
699 //compose.compose(); ### FIXME!
700 image[lastPos].character = compose[0].unicode();*/
701 }
702
scrolledLines() const703 int Screen::scrolledLines() const
704 {
705 return _scrolledLines;
706 }
droppedLines() const707 int Screen::droppedLines() const
708 {
709 return _droppedLines;
710 }
resetDroppedLines()711 void Screen::resetDroppedLines()
712 {
713 _droppedLines = 0;
714 }
resetScrolledLines()715 void Screen::resetScrolledLines()
716 {
717 _scrolledLines = 0;
718 }
719
scrollUp(int n)720 void Screen::scrollUp(int n)
721 {
722 if (n == 0) n = 1; // Default
723 if (_topMargin == 0) addHistLine(); // history.history
724 scrollUp(_topMargin, n);
725 }
726
lastScrolledRegion() const727 QRect Screen::lastScrolledRegion() const
728 {
729 return _lastScrolledRegion;
730 }
731
scrollUp(int from,int n)732 void Screen::scrollUp(int from, int n)
733 {
734 if (n <= 0 || from + n > _bottomMargin) return;
735
736 _scrolledLines -= n;
737 _lastScrolledRegion = QRect(0,_topMargin,columns-1,(_bottomMargin-_topMargin));
738
739 //FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds.
740 moveImage(loc(0,from),loc(0,from+n),loc(columns-1,_bottomMargin));
741 clearImage(loc(0,_bottomMargin-n+1),loc(columns-1,_bottomMargin),' ');
742 }
743
scrollDown(int n)744 void Screen::scrollDown(int n)
745 {
746 if (n == 0) n = 1; // Default
747 scrollDown(_topMargin, n);
748 }
749
scrollDown(int from,int n)750 void Screen::scrollDown(int from, int n)
751 {
752 _scrolledLines += n;
753
754 //FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds.
755 if (n <= 0)
756 return;
757 if (from > _bottomMargin)
758 return;
759 if (from + n > _bottomMargin)
760 n = _bottomMargin - from;
761 moveImage(loc(0,from+n),loc(0,from),loc(columns-1,_bottomMargin-n));
762 clearImage(loc(0,from),loc(columns-1,from+n-1),' ');
763 }
764
setCursorYX(int y,int x)765 void Screen::setCursorYX(int y, int x)
766 {
767 setCursorY(y); setCursorX(x);
768 }
769
setCursorX(int x)770 void Screen::setCursorX(int x)
771 {
772 if (x == 0) x = 1; // Default
773 x -= 1; // Adjust
774 cuX = std::max(0,std::min(columns-1, x));
775 }
776
setCursorY(int y)777 void Screen::setCursorY(int y)
778 {
779 if (y == 0) y = 1; // Default
780 y -= 1; // Adjust
781 cuY = std::max(0,std::min(lines -1, y + (getMode(MODE_Origin) ? _topMargin : 0) ));
782 }
783
home()784 void Screen::home()
785 {
786 cuX = 0;
787 cuY = 0;
788 }
789
toStartOfLine()790 void Screen::toStartOfLine()
791 {
792 cuX = 0;
793 }
794
getCursorX() const795 int Screen::getCursorX() const
796 {
797 return cuX;
798 }
799
getCursorY() const800 int Screen::getCursorY() const
801 {
802 return cuY;
803 }
804
clearImage(int loca,int loce,char c)805 void Screen::clearImage(int loca, int loce, char c)
806 {
807 int scr_TL=loc(0,history->getLines());
808 //FIXME: check positions
809
810 //Clear entire selection if it overlaps region to be moved...
811 if ( (selBottomRight > (loca+scr_TL) )&&(selTopLeft < (loce+scr_TL)) )
812 {
813 clearSelection();
814 }
815
816 int topLine = loca/columns;
817 int bottomLine = loce/columns;
818
819 Character clearCh(c,currentForeground,currentBackground,DEFAULT_RENDITION);
820
821 //if the character being used to clear the area is the same as the
822 //default character, the affected lines can simply be shrunk.
823 bool isDefaultCh = (clearCh == Character());
824
825 for (int y=topLine;y<=bottomLine;y++)
826 {
827 lineProperties[y] = 0;
828
829 int endCol = ( y == bottomLine) ? loce%columns : columns-1;
830 int startCol = ( y == topLine ) ? loca%columns : 0;
831
832 QVector<Character>& line = screenLines[y];
833
834 if ( isDefaultCh && endCol == columns-1 )
835 {
836 line.resize(startCol);
837 }
838 else
839 {
840 if (line.size() < endCol + 1)
841 line.resize(endCol+1);
842
843 Character* data = line.data();
844 for (int i=startCol;i<=endCol;i++)
845 data[i]=clearCh;
846 }
847 }
848 }
849
moveImage(int dest,int sourceBegin,int sourceEnd)850 void Screen::moveImage(int dest, int sourceBegin, int sourceEnd)
851 {
852 Q_ASSERT( sourceBegin <= sourceEnd );
853
854 int lines=(sourceEnd-sourceBegin)/columns;
855
856 //move screen image and line properties:
857 //the source and destination areas of the image may overlap,
858 //so it matters that we do the copy in the right order -
859 //forwards if dest < sourceBegin or backwards otherwise.
860 //(search the web for 'memmove implementation' for details)
861 if (dest < sourceBegin)
862 {
863 for (int i=0;i<=lines;i++)
864 {
865 screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ];
866 lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i];
867 }
868 }
869 else
870 {
871 for (int i=lines;i>=0;i--)
872 {
873 screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ];
874 lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i];
875 }
876 }
877
878 if (lastPos != -1)
879 {
880 int diff = dest - sourceBegin; // Scroll by this amount
881 lastPos += diff;
882 if ((lastPos < 0) || (lastPos >= (lines*columns)))
883 lastPos = -1;
884 }
885
886 // Adjust selection to follow scroll.
887 if (selBegin != -1)
888 {
889 bool beginIsTL = (selBegin == selTopLeft);
890 int diff = dest - sourceBegin; // Scroll by this amount
891 int scr_TL=loc(0,history->getLines());
892 int srca = sourceBegin+scr_TL; // Translate index from screen to global
893 int srce = sourceEnd+scr_TL; // Translate index from screen to global
894 int desta = srca+diff;
895 int deste = srce+diff;
896
897 if ((selTopLeft >= srca) && (selTopLeft <= srce))
898 selTopLeft += diff;
899 else if ((selTopLeft >= desta) && (selTopLeft <= deste))
900 selBottomRight = -1; // Clear selection (see below)
901
902 if ((selBottomRight >= srca) && (selBottomRight <= srce))
903 selBottomRight += diff;
904 else if ((selBottomRight >= desta) && (selBottomRight <= deste))
905 selBottomRight = -1; // Clear selection (see below)
906
907 if (selBottomRight < 0)
908 {
909 clearSelection();
910 }
911 else
912 {
913 if (selTopLeft < 0)
914 selTopLeft = 0;
915 }
916
917 if (beginIsTL)
918 selBegin = selTopLeft;
919 else
920 selBegin = selBottomRight;
921 }
922 }
923
clearToEndOfScreen()924 void Screen::clearToEndOfScreen()
925 {
926 clearImage(loc(cuX,cuY),loc(columns-1,lines-1),' ');
927 }
928
clearToBeginOfScreen()929 void Screen::clearToBeginOfScreen()
930 {
931 clearImage(loc(0,0),loc(cuX,cuY),' ');
932 }
933
clearEntireScreen()934 void Screen::clearEntireScreen()
935 {
936 // Add entire screen to history
937 for (int i = 0; i < (lines-1); i++)
938 {
939 addHistLine(); scrollUp(0,1);
940 }
941
942 clearImage(loc(0,0),loc(columns-1,lines-1),' ');
943 }
944
945 /*! fill screen with 'E'
946 This is to aid screen alignment
947 */
948
helpAlign()949 void Screen::helpAlign()
950 {
951 clearImage(loc(0,0),loc(columns-1,lines-1),'E');
952 }
953
clearToEndOfLine()954 void Screen::clearToEndOfLine()
955 {
956 clearImage(loc(cuX,cuY),loc(columns-1,cuY),' ');
957 }
958
clearToBeginOfLine()959 void Screen::clearToBeginOfLine()
960 {
961 clearImage(loc(0,cuY),loc(cuX,cuY),' ');
962 }
963
clearEntireLine()964 void Screen::clearEntireLine()
965 {
966 clearImage(loc(0,cuY),loc(columns-1,cuY),' ');
967 }
968
setRendition(int re)969 void Screen::setRendition(int re)
970 {
971 currentRendition |= re;
972 updateEffectiveRendition();
973 }
974
resetRendition(int re)975 void Screen::resetRendition(int re)
976 {
977 currentRendition &= ~re;
978 updateEffectiveRendition();
979 }
980
setDefaultRendition()981 void Screen::setDefaultRendition()
982 {
983 setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR);
984 setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR);
985 currentRendition = DEFAULT_RENDITION;
986 updateEffectiveRendition();
987 }
988
setForeColor(int space,int color)989 void Screen::setForeColor(int space, int color)
990 {
991 currentForeground = CharacterColor(space, color);
992
993 if ( currentForeground.isValid() )
994 updateEffectiveRendition();
995 else
996 setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR);
997 }
998
setBackColor(int space,int color)999 void Screen::setBackColor(int space, int color)
1000 {
1001 currentBackground = CharacterColor(space, color);
1002
1003 if ( currentBackground.isValid() )
1004 updateEffectiveRendition();
1005 else
1006 setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR);
1007 }
1008
clearSelection()1009 void Screen::clearSelection()
1010 {
1011 selBottomRight = -1;
1012 selTopLeft = -1;
1013 selBegin = -1;
1014 }
1015
getSelectionStart(int & column,int & line) const1016 void Screen::getSelectionStart(int& column , int& line) const
1017 {
1018 if ( selTopLeft != -1 )
1019 {
1020 column = selTopLeft % columns;
1021 line = selTopLeft / columns;
1022 }
1023 else
1024 {
1025 column = cuX + getHistLines();
1026 line = cuY + getHistLines();
1027 }
1028 }
getSelectionEnd(int & column,int & line) const1029 void Screen::getSelectionEnd(int& column , int& line) const
1030 {
1031 if ( selBottomRight != -1 )
1032 {
1033 column = selBottomRight % columns;
1034 line = selBottomRight / columns;
1035 }
1036 else
1037 {
1038 column = cuX + getHistLines();
1039 line = cuY + getHistLines();
1040 }
1041 }
setSelectionStart(const int x,const int y,const bool mode)1042 void Screen::setSelectionStart(const int x, const int y, const bool mode)
1043 {
1044 selBegin = loc(x,y);
1045 /* FIXME, HACK to correct for x too far to the right... */
1046 if (x == columns) selBegin--;
1047
1048 selBottomRight = selBegin;
1049 selTopLeft = selBegin;
1050 blockSelectionMode = mode;
1051 }
1052
setSelectionEnd(const int x,const int y)1053 void Screen::setSelectionEnd( const int x, const int y)
1054 {
1055 if (selBegin == -1)
1056 return;
1057
1058 int endPos = loc(x,y);
1059
1060 if (endPos < selBegin)
1061 {
1062 selTopLeft = endPos;
1063 selBottomRight = selBegin;
1064 }
1065 else
1066 {
1067 /* FIXME, HACK to correct for x too far to the right... */
1068 if (x == columns)
1069 endPos--;
1070
1071 selTopLeft = selBegin;
1072 selBottomRight = endPos;
1073 }
1074
1075 // Normalize the selection in column mode
1076 if (blockSelectionMode)
1077 {
1078 int topRow = selTopLeft / columns;
1079 int topColumn = selTopLeft % columns;
1080 int bottomRow = selBottomRight / columns;
1081 int bottomColumn = selBottomRight % columns;
1082
1083 selTopLeft = loc(std::min(topColumn,bottomColumn),topRow);
1084 selBottomRight = loc(std::max(topColumn,bottomColumn),bottomRow);
1085 }
1086 }
1087
isSelected(const int x,const int y) const1088 bool Screen::isSelected( const int x,const int y) const
1089 {
1090 bool columnInSelection = true;
1091 if (blockSelectionMode)
1092 {
1093 columnInSelection = x >= (selTopLeft % columns) &&
1094 x <= (selBottomRight % columns);
1095 }
1096
1097 int pos = loc(x,y);
1098 return pos >= selTopLeft && pos <= selBottomRight && columnInSelection;
1099 }
1100
selectedText(bool preserveLineBreaks) const1101 QString Screen::selectedText(bool preserveLineBreaks) const
1102 {
1103 QString result;
1104 QTextStream stream(&result, QIODevice::ReadWrite);
1105
1106 PlainTextDecoder decoder;
1107 decoder.begin(&stream);
1108 writeSelectionToStream(&decoder , preserveLineBreaks);
1109 decoder.end();
1110
1111 return result;
1112 }
1113
isSelectionValid() const1114 bool Screen::isSelectionValid() const
1115 {
1116 return selTopLeft >= 0 && selBottomRight >= 0;
1117 }
1118
writeSelectionToStream(TerminalCharacterDecoder * decoder,bool preserveLineBreaks) const1119 void Screen::writeSelectionToStream(TerminalCharacterDecoder* decoder ,
1120 bool preserveLineBreaks) const
1121 {
1122 if (!isSelectionValid())
1123 return;
1124 writeToStream(decoder,selTopLeft,selBottomRight,preserveLineBreaks);
1125 }
1126
writeToStream(TerminalCharacterDecoder * decoder,int startIndex,int endIndex,bool preserveLineBreaks) const1127 void Screen::writeToStream(TerminalCharacterDecoder* decoder,
1128 int startIndex, int endIndex,
1129 bool preserveLineBreaks) const
1130 {
1131 int top = startIndex / columns;
1132 int left = startIndex % columns;
1133
1134 int bottom = endIndex / columns;
1135 int right = endIndex % columns;
1136
1137 Q_ASSERT( top >= 0 && left >= 0 && bottom >= 0 && right >= 0 );
1138
1139 for (int y=top;y<=bottom;y++)
1140 {
1141 int start = 0;
1142 if ( y == top || blockSelectionMode ) start = left;
1143
1144 int count = -1;
1145 if ( y == bottom || blockSelectionMode ) count = right - start + 1;
1146
1147 const bool appendNewLine = ( y != bottom );
1148 int copied = copyLineToStream( y,
1149 start,
1150 count,
1151 decoder,
1152 appendNewLine,
1153 preserveLineBreaks );
1154
1155 // if the selection goes beyond the end of the last line then
1156 // append a new line character.
1157 //
1158 // this makes it possible to 'select' a trailing new line character after
1159 // the text on a line.
1160 if ( y == bottom &&
1161 copied < count )
1162 {
1163 Character newLineChar('\n');
1164 decoder->decodeLine(&newLineChar,1,0);
1165 }
1166 }
1167 }
1168
copyLineToStream(int line,int start,int count,TerminalCharacterDecoder * decoder,bool appendNewLine,bool preserveLineBreaks) const1169 int Screen::copyLineToStream(int line ,
1170 int start,
1171 int count,
1172 TerminalCharacterDecoder* decoder,
1173 bool appendNewLine,
1174 bool preserveLineBreaks) const
1175 {
1176 //buffer to hold characters for decoding
1177 //the buffer is static to avoid initialising every
1178 //element on each call to copyLineToStream
1179 //(which is unnecessary since all elements will be overwritten anyway)
1180 static const int MAX_CHARS = 1024;
1181 static Character characterBuffer[MAX_CHARS];
1182
1183 assert( count < MAX_CHARS );
1184
1185 LineProperty currentLineProperties = 0;
1186
1187 //determine if the line is in the history buffer or the screen image
1188 if (line < history->getLines())
1189 {
1190 const int lineLength = history->getLineLen(line);
1191
1192 // ensure that start position is before end of line
1193 start = std::min(start,std::max(0,lineLength-1));
1194
1195 // retrieve line from history buffer. It is assumed
1196 // that the history buffer does not store trailing white space
1197 // at the end of the line, so it does not need to be trimmed here
1198 if (count == -1)
1199 {
1200 count = lineLength-start;
1201 }
1202 else
1203 {
1204 count = std::min(start+count,lineLength)-start;
1205 }
1206
1207 // safety checks
1208 assert( start >= 0 );
1209 assert( count >= 0 );
1210 // cppcheck-suppress assertWithSideEffect
1211 assert( (start+count) <= history->getLineLen(line) );
1212
1213 history->getCells(line,start,count,characterBuffer);
1214
1215 if ( history->isWrappedLine(line) )
1216 currentLineProperties |= LINE_WRAPPED;
1217 }
1218 else
1219 {
1220 if ( count == -1 )
1221 count = columns - start;
1222
1223 assert( count >= 0 );
1224
1225 const int screenLine = line-history->getLines();
1226
1227 Character* data = screenLines[screenLine].data();
1228 int length = screenLines[screenLine].count();
1229
1230 //retrieve line from screen image
1231 for (int i=start;i < std::min(start+count,length);i++)
1232 {
1233 characterBuffer[i-start] = data[i];
1234 }
1235
1236 // count cannot be any greater than length
1237 count = qBound(0,count,length-start);
1238
1239 Q_ASSERT( screenLine < lineProperties.count() );
1240 currentLineProperties |= lineProperties[screenLine];
1241 }
1242
1243 // add new line character at end
1244 const bool omitLineBreak = (currentLineProperties & LINE_WRAPPED) ||
1245 !preserveLineBreaks;
1246
1247 if ( !omitLineBreak && appendNewLine && (count+1 < MAX_CHARS) )
1248 {
1249 characterBuffer[count] = '\n';
1250 count++;
1251 }
1252
1253 //decode line and write to text stream
1254 decoder->decodeLine( (Character*) characterBuffer ,
1255 count, currentLineProperties );
1256
1257 return count;
1258 }
1259
writeLinesToStream(TerminalCharacterDecoder * decoder,int fromLine,int toLine) const1260 void Screen::writeLinesToStream(TerminalCharacterDecoder* decoder, int fromLine, int toLine) const
1261 {
1262 writeToStream(decoder,loc(0,fromLine),loc(columns-1,toLine));
1263 }
1264
addHistLine()1265 void Screen::addHistLine()
1266 {
1267 // add line to history buffer
1268 // we have to take care about scrolling, too...
1269
1270 if (hasScroll())
1271 {
1272 int oldHistLines = history->getLines();
1273
1274 history->addCellsVector(screenLines[0]);
1275 history->addLine( lineProperties[0] & LINE_WRAPPED );
1276
1277 int newHistLines = history->getLines();
1278
1279 bool beginIsTL = (selBegin == selTopLeft);
1280
1281 // If the history is full, increment the count
1282 // of dropped lines
1283 if ( newHistLines == oldHistLines )
1284 _droppedLines++;
1285
1286 // Adjust selection for the new point of reference
1287 if (newHistLines > oldHistLines)
1288 {
1289 if (selBegin != -1)
1290 {
1291 selTopLeft += columns;
1292 selBottomRight += columns;
1293 }
1294 }
1295
1296 if (selBegin != -1)
1297 {
1298 // Scroll selection in history up
1299 int top_BR = loc(0, 1+newHistLines);
1300
1301 if (selTopLeft < top_BR)
1302 selTopLeft -= columns;
1303
1304 if (selBottomRight < top_BR)
1305 selBottomRight -= columns;
1306
1307 if (selBottomRight < 0)
1308 clearSelection();
1309 else
1310 {
1311 if (selTopLeft < 0)
1312 selTopLeft = 0;
1313 }
1314
1315 if (beginIsTL)
1316 selBegin = selTopLeft;
1317 else
1318 selBegin = selBottomRight;
1319 }
1320 }
1321
1322 }
1323
getHistLines() const1324 int Screen::getHistLines() const
1325 {
1326 return history->getLines();
1327 }
1328
setScroll(const HistoryType & t,bool copyPreviousScroll)1329 void Screen::setScroll(const HistoryType& t , bool copyPreviousScroll)
1330 {
1331 clearSelection();
1332
1333 if ( copyPreviousScroll )
1334 history = t.scroll(history);
1335 else
1336 {
1337 HistoryScroll* oldScroll = history;
1338 history = t.scroll(0);
1339 delete oldScroll;
1340 }
1341 }
1342
hasScroll() const1343 bool Screen::hasScroll() const
1344 {
1345 return history->hasScroll();
1346 }
1347
getScroll() const1348 const HistoryType& Screen::getScroll() const
1349 {
1350 return history->getType();
1351 }
1352
setLineProperty(LineProperty property,bool enable)1353 void Screen::setLineProperty(LineProperty property , bool enable)
1354 {
1355 if ( enable )
1356 lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | property);
1357 else
1358 lineProperties[cuY] = (LineProperty)(lineProperties[cuY] & ~property);
1359 }
fillWithDefaultChar(Character * dest,int count)1360 void Screen::fillWithDefaultChar(Character* dest, int count)
1361 {
1362 for (int i=0;i<count;i++)
1363 dest[i] = defaultChar;
1364 }
1365