source: ogBrowser-Git/qtermwidget/src/Screen.cpp

qndtest
Last change on this file was 050d67a, checked in by adelcastillo <adelcastillo@…>, 15 years ago

Ahora el browser tiene consola en vez del output.
Pasado todo el sistema de compilacion a cmake.

git-svn-id: https://opengnsys.es/svn/trunk@408 a21b9725-9963-47de-94b9-378ad31fedc9

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