source: ogBrowser-Git/qtermwidget/lib/Screen.cpp @ ffbf8ac

jenkinsmain
Last change on this file since ffbf8ac was 64efc22, checked in by Vadim Troshchinskiy <vtroshchinskiy@…>, 19 months ago

Update qtermwidget to modern version

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