ansiterm.cxx
上传用户:hzhsqp
上传日期:2007-01-06
资源大小:1600k
文件大小:32k
源码类别:

IP电话/视频会议

开发平台:

Visual C++

  1. /*
  2.  * ansiterm.cxx
  3.  *
  4.  * ANSI terminal emulation class.
  5.  *
  6.  * Portable Windows Library
  7.  *
  8.  * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
  9.  *
  10.  * The contents of this file are subject to the Mozilla Public License
  11.  * Version 1.0 (the "License"); you may not use this file except in
  12.  * compliance with the License. You may obtain a copy of the License at
  13.  * http://www.mozilla.org/MPL/
  14.  *
  15.  * Software distributed under the License is distributed on an "AS IS"
  16.  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  17.  * the License for the specific language governing rights and limitations
  18.  * under the License.
  19.  *
  20.  * The Original Code is Portable Windows Library.
  21.  *
  22.  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
  23.  *
  24.  * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
  25.  * All Rights Reserved.
  26.  *
  27.  * Contributor(s): ______________________________________.
  28.  *
  29.  * $Log: ansiterm.cxx,v $
  30.  * Revision 1.40  1998/12/20 09:14:37  robertj
  31.  * Added pragma implementation for GNU compiler.
  32.  *
  33.  * Revision 1.39  1998/12/12 00:44:13  robertj
  34.  * Added capability to get text lines out of scroll back buffer.
  35.  *
  36.  * Revision 1.38  1998/11/30 04:52:12  robertj
  37.  * New directory structure
  38.  *
  39.  * Revision 1.37  1998/10/16 11:08:14  robertj
  40.  * GNU compatibility.
  41.  *
  42.  * Revision 1.36  1998/09/23 06:29:33  robertj
  43.  * Added open source copyright license.
  44.  *
  45.  * Revision 1.35  1998/09/18 13:09:33  robertj
  46.  * Fixed clearing of very bottom of window.
  47.  *
  48.  * Revision 1.34  1998/09/17 04:24:45  robertj
  49.  * Fixed update problems requiring screen refresh to see output text.
  50.  *
  51.  * Revision 1.33  1998/09/04 06:57:02  robertj
  52.  * Fixed crash if window was realy, really wide.
  53.  *
  54.  * Revision 1.32  1997/04/27 05:50:20  robertj
  55.  * DLL support.
  56.  *
  57.  * Revision 1.31  1996/12/21 07:01:35  robertj
  58.  * Fixed flush on close problem in ANSI terminal stream.
  59.  *
  60.  * Revision 1.30  1996/06/03 10:07:31  robertj
  61.  * Fixed bug in auto scroll to cursor position on output.
  62.  * Fixed bug in DEL character processing (no screen update).
  63.  *
  64.  * Revision 1.29  1996/05/15 10:14:40  robertj
  65.  * Rewrite of terminal to make descendant only require single character output function.
  66.  *
  67.  * Revision 1.28  1996/04/30 12:33:45  robertj
  68.  * Changed "inPixels" boolean to enum for three coordinate systems.
  69.  *
  70.  * Revision 1.27  1996/04/29 12:18:24  robertj
  71.  * Added local echo capability to terminal.
  72.  *
  73.  * Revision 1.26  1995/12/10 11:36:16  robertj
  74.  * Fixed bug in display of window bigger than virtual screen.
  75.  * Fixed warning in GNU compiler.
  76.  *
  77.  * Revision 1.25  1995/11/20 11:04:09  robertj
  78.  * Fixed bugs in non-adjustable rows and columns mode.
  79.  * Implemented horizontal scrolling.
  80.  *
  81.  * Revision 1.24  1995/11/09 12:19:50  robertj
  82.  * Removed GNU warning.
  83.  *
  84.  * Revision 1.23  1995/08/24 12:35:51  robertj
  85.  * Implemented copy/paste.
  86.  *
  87.  * Revision 1.22  1995/07/31 12:15:47  robertj
  88.  * Removed PContainer from PChannel ancestor.
  89.  *
  90.  * Revision 1.21  1995/07/02 01:20:58  robertj
  91.  * Removed OnDoubleClick() and added BOOL to OnMouseDown() for double click.
  92.  *
  93.  * Revision 1.20  1995/06/17 11:11:09  robertj
  94.  * Changed OnKeyDown to return BOOL indicating OnKeyInput is to be used.
  95.  *
  96.  * Revision 1.19  1995/06/04 08:41:42  robertj
  97.  * Some minor speedups.
  98.  *
  99.  * Revision 1.18  1995/04/25 11:28:06  robertj
  100.  * Fixed Borland compiler warnings.
  101.  *
  102.  * Revision 1.17  1995/04/01 08:30:08  robertj
  103.  * Changed default end of line wrap option.
  104.  * Fixed loss of focus problem.
  105.  *
  106.  * Revision 1.16  1995/01/07 04:39:44  robertj
  107.  * Redesigned font enumeration code and changed font styles.
  108.  *
  109.  * Revision 1.15  1995/01/06  10:43:53  robertj
  110.  * Changed PRealFont usage from pointer to reference.
  111.  *
  112.  * Revision 1.14  1994/12/05  11:20:22  robertj
  113.  * Removed Normalise() function from PRect as all rectangles should be inherently normal.
  114.  *
  115.  * Revision 1.13  1994/11/28  12:35:42  robertj
  116.  * Fixed closure of channel not clearing the keyboard stream.
  117.  * Moved saved cursor position variables to PAnsiTerminal.
  118.  *
  119.  * Revision 1.12  1994/10/30  11:52:02  robertj
  120.  * Fixed bug in auto scrolling vertical scroll bar position.
  121.  *
  122.  * Revision 1.11  1994/10/23  03:40:23  robertj
  123.  * Fixed scrolling wrong direction.
  124.  * Added ReturnOnLineFeed option.
  125.  * Added capability of setting cursor type.
  126.  * Improved the channel.
  127.  *
  128.  * Revision 1.10  1994/09/25  10:48:32  robertj
  129.  * Added boundary condition tests.
  130.  *
  131.  * Revision 1.9  1994/08/21  23:43:02  robertj
  132.  * Bug fixes.
  133.  *
  134.  * Revision 1.8  1994/08/01  03:41:24  robertj
  135.  * Use of PNEW instead of new for heap debugging. Need undef for Unix end.
  136.  *
  137.  * Revision 1.7  1994/07/27  05:58:07  robertj
  138.  * Synchronisation.
  139.  *
  140.  * Revision 1.6  1994/07/25  03:38:07  robertj
  141.  * Changed variables to int for better portability.
  142.  *
  143.  * Revision 1.5  1994/07/21  12:33:49  robertj
  144.  * Moved cooperative threads to common.
  145.  *
  146.  * Revision 1.4  1994/07/17  10:46:06  robertj
  147.  * Major redesign to get list instead of array for line data.
  148.  *
  149.  * Revision 1.3  1994/06/25  11:55:15  robertj
  150.  * Unix version synchronisation.
  151.  *
  152.  * Revision 1.2  1994/04/20  12:17:44  robertj
  153.  * some implementation
  154.  *
  155.  * Revision 1.1  1994/04/01  14:27:01  robertj
  156.  * Initial revision
  157.  */
  158. #ifdef __GNUC__
  159. #pragma implementation "ansiterm.h"
  160. #endif
  161. #include <pwlib.h>
  162. #include <pwclib/ansiterm.h>
  163. #include <limits.h>
  164. #define new PNEW
  165. PTerminal::PTerminal(PScroller * parent, ostream * kbd)
  166.   : PScrollable(parent)
  167. {
  168.   keyboard = kbd;
  169.   PScrollable::SetFont(PFont("Courier", 10));
  170.   SetCursor(PCursor(PSTD_ID_CURSOR_IBEAM));
  171.   variableRowColumns = TRUE;
  172.   blockCursor = TRUE;
  173.   lineFeedOnReturn = FALSE;
  174.   returnOnLineFeed = FALSE;
  175.   localEcho = FALSE;
  176.   wrapEOL = FALSE;
  177.   columns = 80;
  178.   rows = 25;
  179.   maxSavedRows = 0;
  180.   columnModeSelection = FALSE;
  181.   updateColumn = 0;
  182.   Reset();
  183.   GrabFocus();
  184.   ShowCaret();
  185. }
  186. PTerminal::Row::Row(Attribute attribute)
  187. {
  188.   for (PINDEX i = 0; i < MaxColumns; i++)
  189.     attrib[i] = attribute;
  190.   memset(ascii, ' ', sizeof(ascii));
  191. }
  192. void PTerminal::Reset()
  193. {
  194.   cellWidth = font.GetAvgWidth(TRUE);
  195.   cellHeight = font.GetHeight(TRUE);
  196.   SetScaleFactor(cellWidth, cellHeight);
  197.   cursorRow = cursorColumn = 0;
  198.   caret.SetDimensions(blockCursor ? cellWidth : 1, cellHeight);
  199.   SetCaretPos(0, 0, PixelCoords);
  200.   PDim dim = GetDimensions(PixelCoords);
  201.   columnsVisible = dim.Width()/cellWidth;
  202.   if (columnsVisible > Row::MaxColumns)
  203.     columnsVisible = Row::MaxColumns;
  204.   rowsVisible = dim.Height()/cellHeight;
  205.   if (variableRowColumns) {
  206.     columns = columnsVisible > 1 ? columnsVisible : 2;
  207.     rows = rowsVisible > 1 ? rowsVisible : 2;
  208.   }
  209.   scrollTop = 0;
  210.   scrollBottom = rows;
  211.   attribute.bold = FALSE;
  212.   attribute.underline = FALSE;
  213.   attribute.blink = FALSE;
  214.   attribute.inverse = FALSE;
  215.   attribute.selected = FALSE;
  216.   attribute.fgColor = 8; // Window fg colour
  217.   attribute.bkColor = 9; // Window bk colour
  218.   rowData.RemoveAll();
  219.   for (PINDEX i = 0; i < rows; i++)
  220.     rowData.Append(new Row(attribute));
  221.   SetScrollRange(PRect(0, 0, columns, rows));
  222.   anchor = selection = 0;
  223.   Invalidate();
  224. }
  225. void PTerminal::SetFont(const PFont & newFont, BOOL toChildren)
  226. {
  227.   PScrollable::SetFont(newFont, toChildren);
  228.   cellWidth = font.GetAvgWidth(TRUE);
  229.   cellHeight = font.GetHeight(TRUE);
  230.   SetScaleFactor(cellWidth, cellHeight);
  231.   caret.SetDimensions(blockCursor ? cellWidth : 1, cellHeight);
  232.   SetDimensions(GetDimensions(PixelCoords), PixelCoords);
  233.   Invalidate();
  234. }
  235. void PTerminal::_SetDimensions(PDIMENSION width, PDIMENSION height,
  236.                                CoordinateSystem coords)
  237. {
  238.   PScrollable::_SetDimensions(width, height, coords);
  239.   PDim dim = GetDimensions(PixelCoords);
  240.   columnsVisible = dim.Width()/cellWidth;
  241.   if (columnsVisible > Row::MaxColumns)
  242.     columnsVisible = Row::MaxColumns;
  243.   rowsVisible = dim.Height()/cellHeight;
  244.   if (variableRowColumns) {
  245.     variableRowColumns = FALSE;
  246.     SetColumns(columnsVisible);
  247.     SetRows(rowsVisible);
  248.     variableRowColumns = TRUE;
  249.   }
  250.   else
  251.     SetScrollRange(PRect(0, 0, columns, rowData.GetSize()));
  252. }
  253. void PTerminal::Row::AdjustSelection(PINDEX start, PINDEX finish, BOOL select)
  254. {
  255.   for (PINDEX col = start; col < finish; col++)
  256.     attrib[col].selected = select;
  257. }
  258. void PTerminal::AdjustSelection(PCanvas & canvas,
  259.                                       PINDEX start, PINDEX finish, BOOL select)
  260. {
  261.   if (start == finish)
  262.     return;
  263.   if (start > finish) {
  264.     PINDEX tmp = start;
  265.     start = finish;
  266.     finish = tmp;
  267.   }
  268.   PINDEX firstRow = start/Row::MaxColumns;
  269.   PINDEX lastRow = finish/Row::MaxColumns;
  270.   PINDEX firstCol = start%Row::MaxColumns;
  271.   PINDEX lastCol = finish%Row::MaxColumns;
  272.   if (columnModeSelection) {
  273.     if (firstCol > lastCol) {
  274.       PINDEX tmp = lastCol;
  275.       lastCol = firstCol;
  276.       firstCol = tmp;
  277.     }
  278.     for (PINDEX row = firstRow; row <= lastRow; row++) {
  279.       rowData[row].AdjustSelection(0, Row::MaxColumns, FALSE);
  280.       if (select)
  281.         rowData[row].AdjustSelection(firstCol, lastCol, select);
  282.       rowData[row].Draw(this, canvas, row - origin.Y(), 0);
  283.     }
  284.   }
  285.   else {
  286.     for (PINDEX row = firstRow; row <= lastRow; row++) {
  287.       rowData[row].AdjustSelection(firstCol,
  288.                        row < lastRow ? (int)Row::MaxColumns : lastCol, select);
  289.       rowData[row].Draw(this, canvas, row - origin.Y(), 0);
  290.       firstCol = 0;
  291.     }
  292.   }
  293. }
  294. void PTerminal::OnMouseTrack(PCanvas * canvas,
  295.                                           const PPoint & where, BOOL lastTrack)
  296. {
  297.   PINDEX pos = origin.Y()*Row::MaxColumns + origin.X();
  298.   if (where.Y() > 0)
  299.     pos += (where.Y()/cellHeight)*Row::MaxColumns;
  300.   if (where.X() > 0)
  301.     pos += (where.X() + cellWidth/2)/cellWidth;
  302.   if (pos >= rowData.GetSize()*Row::MaxColumns)
  303.     pos = rowData.GetSize()*Row::MaxColumns - 1;
  304.   if (pos == selection)
  305.     return;
  306.   if ((pos >= anchor && selection < anchor) ||
  307.       (pos <= anchor && selection > anchor)) {
  308.     AdjustSelection(*canvas, anchor, selection, FALSE);
  309.     selection = anchor;
  310.   }
  311.   if (columnModeSelection) {
  312.     if ((pos > anchor && pos < selection) ||
  313.         (pos < anchor && pos > selection))
  314.       AdjustSelection(*canvas, selection, pos, FALSE);
  315.     AdjustSelection(*canvas, anchor, pos, TRUE);
  316.   }
  317.   else
  318.     AdjustSelection(*canvas, pos, selection,
  319.        (pos > anchor && pos > selection) || (pos < anchor && pos < selection));
  320.   selection = pos;
  321.   if (lastTrack)
  322.     UpdateCommandSources();
  323. }
  324. void PTerminal::OnMouseDown(PKeyCode button, const PPoint & where, BOOL)
  325. {
  326.   GrabFocus();
  327.   PCanvas * canvas = StartMouseTrack(TRUE);
  328.   PINDEX pos = (origin.Y() + where.Y()/cellHeight)*Row::MaxColumns +
  329.                                 origin.X() + (where.X()+cellWidth/2)/cellWidth;
  330.   if (pos >= rowData.GetSize()*Row::MaxColumns)
  331.     pos = rowData.GetSize()*Row::MaxColumns - 1;
  332.   if (HasSelection() && button.IsModifier(PKeyCode::Shift)) {
  333.     if ((anchor > selection && pos > anchor) ||
  334.         (anchor < selection && pos < anchor)) {
  335.       PINDEX tmp = anchor;
  336.       anchor = selection;
  337.       selection = tmp;
  338.     }
  339.     OnMouseTrack(canvas, where, FALSE);
  340.   }
  341.   else {
  342.     AdjustSelection(*canvas, anchor, selection, FALSE);
  343.     anchor = selection = pos;
  344.     columnModeSelection = button.IsModifier(PKeyCode::Control);
  345.   }
  346. }
  347. void PTerminal::OnKeyInput(const PString & str)
  348. {
  349.   if (keyboard != NULL) {
  350.     *keyboard << str;
  351.     keyboard->flush();
  352.     if (localEcho)
  353.       Write(str);
  354.   }
  355. }
  356. void PTerminal::Row::Draw(PTerminal * term,
  357.                                  PCanvas & canvas, PINDEX line, PINDEX lastCol)
  358. {
  359.   static PColour colour[10] = {
  360.     PColour::Black,
  361.     PColour::Red,
  362.     PColour::Yellow,
  363.     PColour::Green,
  364.     PColour::Blue,
  365.     PColour::Magenta,
  366.     PColour::Cyan,
  367.     PColour::White,
  368.     PApplication::Current().GetWindowFgColour(),
  369.     PApplication::Current().GetWindowBkColour()
  370.   };
  371.   PDIMENSION origins[MaxColumns];
  372.   for (int i = 0; i < MaxColumns; i++)
  373.     origins[i] = term->cellWidth;
  374.   WORD lastAttr = *(WORD *)&attrib[0];
  375.   PINDEX col = lastCol;
  376.   while (lastCol < term->columnsVisible) {
  377.     col++;
  378.     if (lastAttr != *(WORD *)&attrib[col] || col >= term->columnsVisible) {
  379.       if (col > lastCol) {
  380.         Attribute attr = attrib[lastCol];
  381.         if (attr.inverse ^ attr.selected) {
  382.           canvas.SetTextFgColour(colour[attr.bkColor]);
  383.           canvas.SetTextBkColour(colour[attr.fgColor]);
  384.         }
  385.         else {
  386.           canvas.SetTextFgColour(colour[attr.fgColor]);
  387.           canvas.SetTextBkColour(colour[attr.bkColor]);
  388.         }
  389.         WORD styles = PFont::Regular;
  390.         if (attr.bold)
  391.           styles |= PFont::Bold;
  392.         if (attr.underline)
  393.           styles |= PFont::Underline;
  394.         PFont newFont(term->font.GetFacename(), term->font.GetSize(), styles);
  395.         canvas.SetFont(newFont);
  396.         canvas.DrawTextLine(lastCol*term->cellWidth,
  397.               line*term->cellHeight, &ascii[lastCol], origins, col - lastCol);
  398.       }
  399.       lastAttr = *(WORD *)&attrib[col];
  400.       lastCol = col;
  401.     }
  402.   }
  403. }
  404. void PTerminal::OnRedraw(PCanvas & canvas)
  405. {
  406.   HideCaret();
  407.   canvas.SetMappingRect(canvas.GetViewportRect());
  408.   canvas.SetOrigin(PPoint(origin.X()*scaleFactor.X(), 0));
  409.   PINDEX lastRow = PMIN(rowData.GetSize(), rowsVisible);
  410.   PINDEX bottom = origin.Y() + lastRow;
  411.   canvas.SetPenFgColour(GetBackgroundColour());
  412.   canvas.DrawRect(0, lastRow*cellHeight, 30000, 30000);
  413.   for (PINDEX line = 1; line <= lastRow; line++)
  414.     rowData[bottom - line].Draw(this, canvas, lastRow - line, 0);
  415.   ShowCaret();
  416. }
  417. PString PTerminal::GetRow(PINDEX idx) const
  418. {
  419.   if (idx >= GetTotalRows())
  420.     return PString();
  421.   return rowData[idx].GetText();
  422. }
  423. BOOL PTerminal::HasSelection() const
  424. {
  425.   return anchor != selection;
  426. }
  427. PString PTerminal::Row::GetText() const
  428. {
  429.   PINDEX len = sizeof(ascii)-1;
  430.   while (len > 0 && ascii[len] == ' ')
  431.     len--;
  432.   return PString(ascii, len);
  433. }
  434. PString PTerminal::Row::GetSelection() const
  435. {
  436.   PINDEX firstCol = 0;
  437.   while (firstCol < MaxColumns && !attrib[firstCol].selected)
  438.     firstCol++;
  439.   PINDEX lastCol = MaxColumns - 1;
  440.   while (lastCol > firstCol && (!attrib[lastCol].selected || ascii[lastCol] == ' '))
  441.     lastCol--;
  442.   if (lastCol >= firstCol)
  443.     return PString(&ascii[firstCol], lastCol - firstCol + 1);
  444.   else
  445.     return PString();
  446. }
  447. PString PTerminal::GetSelection() const
  448. {
  449.   PString str;
  450.   if (HasSelection()) {
  451.     PINDEX row = PMIN(anchor, selection)/Row::MaxColumns;
  452.     PINDEX finish = PMAX(anchor, selection)/Row::MaxColumns;
  453.     while (row <= finish)
  454.       str += rowData[row++].GetSelection() + "rn";
  455.   }
  456.   return str;
  457. }
  458. void PTerminal::SetSelection(PINDEX startRow, PINDEX startColumn,
  459.                              PINDEX finishRow, PINDEX finishColumn)
  460. {
  461.   anchor = startRow*Row::MaxColumns + startColumn;
  462.   selection = finishRow*Row::MaxColumns + finishColumn;
  463.   Invalidate();
  464. }
  465. void PTerminal::SetKeyboardStream(ostream * kbd)
  466. {
  467.   keyboard = kbd;
  468. }
  469. void PTerminal::SetMaxSavedRows(PINDEX newRows)
  470. {
  471.   maxSavedRows = newRows;
  472.   if (maxSavedRows != 0 && rowData.GetSize() > maxSavedRows+rows)
  473.     rowData.SetSize(maxSavedRows+rows);
  474. }
  475. void PTerminal::SetVariableRowColumns(BOOL b)
  476. {
  477.   variableRowColumns = b;
  478.   if (variableRowColumns)
  479.     SetDimensions(GetDimensions(PixelCoords), PixelCoords);
  480.   else {
  481.     SetRows(rowsVisible > 0 ? rowsVisible : 25);
  482.     SetColumns(columnsVisible > 1 ? columnsVisible : (int)Row::MaxColumns);
  483.   }
  484. }
  485. void PTerminal::SetRows(PINDEX newRows)
  486. {
  487.   if (variableRowColumns) {
  488.     SetDimensions(GetDimensions(PixelCoords), PixelCoords);
  489.     return;
  490.   }
  491.   if (newRows < 2)
  492.     newRows = 2;
  493.   if (scrollTop >= newRows)
  494.     scrollTop = newRows - 1;
  495.   if (scrollBottom == rows || scrollBottom > newRows)
  496.     scrollBottom = newRows;
  497.   if (newRows <= rows)
  498.     AdjustCaretPosition((int)newRows - (int)rows, 0);
  499.   else {
  500.     for (PINDEX i = rows; i < newRows; i++)
  501.       rowData.Append(new Row(attribute));
  502.     SetScrollRange(PRect(0, 0, columns, rowData.GetSize()));
  503.   }
  504.   rows = newRows;
  505. }
  506. void PTerminal::SetColumns(PINDEX newColumns)
  507. {
  508.   if (variableRowColumns) {
  509.     SetDimensions(GetDimensions(PixelCoords), PixelCoords);
  510.     return;
  511.   }
  512.   if (newColumns < 2)
  513.     newColumns = 2;
  514.   else if (newColumns > Row::MaxColumns)
  515.     newColumns = Row::MaxColumns;
  516.   if (columns != newColumns)
  517.     SetScrollRange(PRect(0, 0, columns, rowData.GetSize()));
  518.   columns = newColumns;
  519. }
  520. void PTerminal::AdjustCaretPosition(int dLine, int dCol)
  521. {
  522.   cursorColumn += dCol;
  523.   if ((int)cursorColumn < 0)
  524.     cursorColumn = 0;
  525.   else if (cursorColumn >= columns)
  526.     cursorColumn = columns-1;
  527.   cursorRow += dLine;
  528.   if ((int)cursorRow < (int)scrollTop)   // Need signed compare here
  529.     cursorRow = scrollTop;
  530.   else if (cursorRow >= scrollBottom)
  531.     cursorRow = scrollBottom-1;
  532.   SetCaretPos(cursorColumn*cellWidth,
  533.               blockCursor ? cursorRow*cellHeight : (cursorRow+1)*cellHeight-1,
  534.               PixelCoords);
  535. }
  536. void PTerminal::MoveCaretPosition(PDrawCanvas & canvas, int dLine, int dCol)
  537. {
  538.   PINDEX absRow = rowData.GetSize() - rows + cursorRow;
  539.   rowData[absRow].Draw(this, canvas, absRow - origin.Y(), updateColumn);
  540.   AdjustCaretPosition(dLine, dCol);
  541.   updateColumn = cursorColumn;
  542.   if (absRow < (PINDEX)origin.Y() ||
  543.       absRow >= (PINDEX)origin.Y()+rowsVisible ||
  544.       cursorColumn < (PINDEX)origin.X() ||
  545.       cursorColumn >= (PINDEX)origin.X()+columnsVisible)
  546.     ScrollTo(cursorColumn-columnsVisible, cursorRow);
  547. }
  548. void PTerminal::SetBlockCursor(BOOL b)
  549. {
  550.   blockCursor = b;
  551.   caret.SetDimensions(blockCursor ? cellWidth : 1, cellHeight);
  552. }
  553. PTerminal::Row & PTerminal::GetCursorRow()
  554. {
  555.   return rowData[rowData.GetSize() - rows + cursorRow];
  556. }
  557. void PTerminal::ScrollLines(PDrawCanvas & canvas,
  558.                                           PINDEX top, PINDEX bottom, int lines)
  559. {
  560.   PAssert(lines != 0 && bottom > top, PInvalidParameter);
  561.   HideCaret();
  562.   Update();           // Make sure interactor is up to date (fully redrawn)
  563.   MoveCaretPosition(canvas, 0, 0);
  564.   PINDEX totalRows = rowData.GetSize();
  565.   int topRow = totalRows - rows + top;
  566.   int bottomRow = totalRows - rows + bottom;
  567.   int absLines = PABS(lines);
  568.   if (absLines > (int)(bottom - top))
  569.     absLines = bottom - top;
  570.   if (lines > 0 && top == 0) {
  571.     while (absLines-- > 0)
  572.       rowData.InsertAt(bottomRow, new Row(attribute));
  573.     if (maxSavedRows != 0 && rowData.GetSize() > maxSavedRows+rows)
  574.       rowData.SetSize(maxSavedRows+rows);
  575.     if (rowData.GetSize() != totalRows) {
  576.       SetScrollRange(PRect(0, 0, columns, rowData.GetSize()));
  577.       ScrollTo(0, rowData.GetSize());
  578.     }
  579.   }
  580.   else {
  581.     PRect scrollRect(0, top*cellHeight, 30000, (bottom - top)*cellHeight);
  582.     PDIMENSION scrollAmount = absLines*cellHeight;
  583.     BOOL doScroll = scrollAmount < scrollRect.Height();
  584.     if (lines > 0) {
  585.       while (absLines-- > 0) {
  586.         rowData.InsertAt(bottomRow, new Row(attribute));
  587.         rowData.RemoveAt(topRow);
  588.       }
  589.       if (doScroll) {
  590.         PDrawCanvas canvas(this, TRUE);
  591.         canvas.Scroll(0, -(PORDINATE)scrollAmount, scrollRect);
  592.         scrollRect.SetTop(scrollRect.Bottom() - scrollAmount);
  593.       }
  594.     }
  595.     else {
  596.       while (absLines-- > 0) {
  597.         rowData.RemoveAt(bottomRow-1);
  598.         rowData.InsertAt(topRow, new Row(attribute));
  599.       }
  600.       if (doScroll) {
  601.         PDrawCanvas canvas(this, TRUE);
  602.         canvas.Scroll(0, scrollAmount, scrollRect);
  603.         scrollRect.SetHeight(scrollAmount);
  604.       }
  605.     }
  606.     Invalidate(scrollRect, PixelCoords);
  607.   }
  608.   ShowCaret();
  609. }
  610. void PTerminal::Row::Scroll(PTerminal * term, PCanvas & canvas,
  611.                            PINDEX line, PINDEX left, PINDEX right, int columns)
  612. {
  613.   PAssert(columns != 0 && right > left, PInvalidParameter);
  614.   if (right >= MaxColumns)
  615.     right = MaxColumns-1;
  616.   PINDEX absCols = PABS(columns);
  617.   if (absCols > right - left)
  618.     absCols = right - left;
  619.   if (columns < 0) {
  620.     memmove(&attrib[left+absCols], &attrib[left], (right-left-absCols)*sizeof(attrib[0]));
  621.     for (PINDEX i = 0; i < absCols; i++)
  622.       attrib[left+i] = term->attribute;
  623.     memmove(&ascii[left+absCols], &ascii[left], (right-left-absCols)*sizeof(ascii[0]));
  624.     memset(&ascii[left], ' ', absCols*sizeof(ascii[0]));
  625.   }
  626.   else {
  627.     memmove(&attrib[left], &attrib[left+absCols], (right-left-absCols)*sizeof(attrib[0]));
  628.     for (PINDEX i = 0; i < absCols; i++)
  629.       attrib[right-absCols+i] = term->attribute;
  630.     memmove(&ascii[left], &ascii[left+absCols], (right-left-absCols)*sizeof(ascii[0]));
  631.     memset(&ascii[right-absCols], ' ', absCols*sizeof(ascii[0]));
  632.   }
  633.   Draw(term, canvas, line, left);
  634. }
  635. void PTerminal::Write(const char * buf, PINDEX len)
  636. {
  637.   if (len <= 0 || (len == 1 && *buf == ''))
  638.     return;
  639.   PDrawCanvas updateCanvas(this, TRUE);
  640.   updateCanvas.SetOrigin(PPoint(origin.X()*scaleFactor.X(), 0));
  641.   HideCaret();
  642.   MoveCaretPosition(updateCanvas, 0, 0);
  643.   for (PINDEX i = 0; i < len; i++)
  644.     ProcessCharacter(updateCanvas, *buf++);
  645.   MoveCaretPosition(updateCanvas, 0, 0);
  646.   ShowCaret();
  647. }
  648. void PTerminal::ProcessCharacter(PDrawCanvas & canvas, char ch)
  649. {
  650.   switch (ch) {
  651.     case 'a' :
  652.       PSound::Beep();
  653.       break;
  654.     case 'b' :
  655.       MoveCaretPosition(canvas, 0, -1);
  656.       break;
  657.     case '13' : // ^K
  658.       MoveCaretPosition(canvas, -1, 0);
  659.       break;
  660.     case '14' : // ^L
  661.       MoveCaretPosition(canvas, 0, 1);
  662.       break;
  663.     case 't' :
  664.       MoveCaretPosition(canvas, 0, 8 - cursorColumn%8);
  665.       break;
  666.     case 'r' :
  667.       MoveCaretPosition(canvas, 0, -(int)cursorColumn);
  668.       if (!lineFeedOnReturn)
  669.         break;
  670.       // Else do line feed case
  671.     case 'n' :
  672.       if (cursorRow < scrollBottom-1)
  673.         MoveCaretPosition(canvas, 1, 0);
  674.       else
  675.         ScrollLines(canvas, scrollTop, scrollBottom, 1);
  676.       if (returnOnLineFeed)
  677.         MoveCaretPosition(canvas, 0, -(int)cursorColumn);
  678.       break;
  679.     case 'x7f' : // DEL
  680.       if (cursorColumn > 0) {
  681.         MoveCaretPosition(canvas, 0, -1);
  682.         GetCursorRow().SetCell(cursorColumn, ' ', attribute);
  683.       }
  684.       break;
  685.     default :
  686.       GetCursorRow().SetCell(cursorColumn, ch, attribute);
  687.       if (cursorColumn < columns-1)
  688.         AdjustCaretPosition(0, 1);
  689.       else if (wrapEOL) {
  690.         ProcessCharacter(canvas, 'r');
  691.         ProcessCharacter(canvas, 'n');
  692.       }
  693.   }
  694. }
  695. ///////////////////////////////////////////////////////////////////////////////
  696. PAnsiTerminal::PAnsiTerminal(PScroller * parent, ostream * kbd)
  697.   : PTerminal(parent, kbd)
  698. {
  699.   Reset();
  700. }
  701. void PAnsiTerminal::Reset()
  702. {
  703.   PTerminal::Reset();
  704.   inEscapeSequence = FALSE;
  705.   savedCursorRow = savedCursorColumn = 0;
  706. }
  707. BOOL PAnsiTerminal::OnKeyDown(PKeyCode key, unsigned repeat)
  708. {
  709.   static const struct {
  710.     PKeyCode::Value value;
  711.     const char * sequence;
  712.   } ansikeys[] = {
  713.     { PKeyCode::F1,       "33OP" },
  714.     { PKeyCode::F2,       "33OQ" },
  715.     { PKeyCode::F3,       "33OR" },
  716.     { PKeyCode::F4,       "33OS" },
  717.     { PKeyCode::Home,     "33[H" },
  718.     { PKeyCode::Separator,"33Ol" },
  719.     { PKeyCode::Subtract, "33Om" },
  720.     { PKeyCode::Decimal,  "33On" },
  721.     { PKeyCode::KP0,      "33Op" },
  722.     { PKeyCode::KP1,      "33Oq" },
  723.     { PKeyCode::KP2,      "33Or" },
  724.     { PKeyCode::KP3,      "33Os" },
  725.     { PKeyCode::KP4,      "33Ot" },
  726.     { PKeyCode::KP5,      "33Ou" },
  727.     { PKeyCode::KP6,      "33Ov" },
  728.     { PKeyCode::KP7,      "33Ow" },
  729.     { PKeyCode::KP8,      "33Ox" },
  730.     { PKeyCode::KP9,      "33Oy" },
  731.     { PKeyCode::Up,       "33[A" },
  732.     { PKeyCode::Down,     "33[B" },
  733.     { PKeyCode::Left,     "33[D" },
  734.     { PKeyCode::Right,    "33[C" },
  735.     { PKeyCode::Delete,   "177" },
  736.   };
  737.   if (keyboard != NULL && key.GetModifiers() == PKeyCode::NoModifier) {
  738.     for (PINDEX i = 0; i < PARRAYSIZE(ansikeys); i++) {
  739.       if (key.GetValue() == ansikeys[i].value) {
  740.         do {
  741.           *keyboard << ansikeys[i].sequence;
  742.         } while (repeat-- > 0);
  743.         keyboard->flush();
  744.         break;
  745.       }
  746.     }
  747.   }
  748.   return PTerminal::OnKeyDown(key, repeat);
  749. }
  750. void PAnsiTerminal::ProcessCharacter(PDrawCanvas & canvas, char ch)
  751. {
  752.   if (!inEscapeSequence) {
  753.     if (ch != '33')
  754.       PTerminal::ProcessCharacter(canvas, ch);
  755.     else {
  756.       inEscapeSequence = TRUE;
  757.       inParameter = FALSE;
  758.       numParameters = 0;
  759.       memset(parameter, 0, sizeof(parameter));
  760.     }
  761.     return;
  762.   }
  763.   if (inParameter && ch >= '0' && ch <= '9') {
  764.     parameter[numParameters] = parameter[numParameters]*10 + ch-'0';
  765.     return;
  766.   }
  767.   switch (ch) {
  768.     case '33' : // <esc>
  769.       inParameter = FALSE;
  770.       numParameters = 0;
  771.       memset(parameter, 0, sizeof(parameter));
  772.       return;
  773.     case '?' : // <esc>?
  774.       return;
  775.     case '[' : // <esc>[
  776.       inParameter = TRUE;
  777.       return;
  778.     case ';' :  // <esc>[0;1;2; etc
  779.       numParameters++;
  780.       return;
  781.     case 'h' : // <esc>[0h  Set Mode
  782.     case 'l' : // <esc>[0l  Reset Mode
  783.       switch (parameter[0]) {
  784.         case 7 :
  785.           wrapEOL = ch == 'h';
  786.           break;
  787.         case 20 :
  788.           lineFeedOnReturn = ch == 'h';
  789.           break;
  790.       }
  791.       break;
  792.     case 'r' : // <esc>[0;24r  Set scroll region
  793.       if (parameter[0] != 0)
  794.         parameter[0]--;
  795.       if (parameter[1] == 0)
  796.         parameter[1] = rows;
  797.       if (parameter[0] < parameter[1]) {
  798.         scrollTop = PMIN(parameter[0], rows-1);
  799.         scrollBottom = PMIN(parameter[1], rows);
  800.       }
  801.       break;
  802.     case 'A' : // <esc>[0A  Cursor Up
  803.       MoveCaretPosition(canvas, -PMAX(parameter[0], 1), 0);
  804.       break;
  805.     case 'B' : // <esc>[0B  Cursor Down
  806.       MoveCaretPosition(canvas, PMAX(parameter[0], 1), 0);
  807.       break;
  808.     case 'C' : // <esc>[0C  Cursor Forward
  809.       MoveCaretPosition(canvas, 0, PMAX(parameter[0], 1));
  810.       break;
  811.     case 'D' :
  812.       if (inParameter) // <esc>[0D  Cursor Backward
  813.         MoveCaretPosition(canvas, 0, -PMAX(parameter[0], 1));
  814.       else // <esc>D  Scroll Forward
  815.         ScrollLines(canvas, scrollTop, scrollBottom, 1);
  816.       break;
  817.     case 'H' : // <esc>[1;1H  Cursor motion
  818.     case 'f' : // <esc>[1;1f  Set active position
  819.       MoveCaretPosition(canvas,
  820.                        parameter[0]-cursorRow-1, parameter[1]-cursorColumn-1);
  821.       break;
  822.     case 'J' : // <esc>[0J  Erase display
  823.       switch (parameter[0]) {
  824.         case 0 : // <esc>[0J  To end
  825.           if (cursorRow != 0 || cursorColumn != 0) {
  826.             GetCursorRow().Scroll(this,
  827.                            canvas, cursorRow, cursorColumn, INT_MAX, INT_MAX);
  828.             if (cursorRow+1 < rows)
  829.               ScrollLines(canvas, cursorRow+1, rows, INT_MAX);
  830.             break;
  831.           }
  832.           // Do whole screen if cursor is homed
  833.         case 2 : // <esc>[2J  Whole screen
  834.           ScrollLines(canvas, 0, rows, INT_MAX);
  835.           break;
  836.         case 1 : // <esc>[1J  To beginning
  837.           GetCursorRow().Scroll(this,
  838.                                 canvas, cursorRow, 0, cursorColumn+1, INT_MAX);
  839.           if (cursorRow > 0)
  840.             ScrollLines(canvas, 0, cursorRow, INT_MAX);
  841.           break;
  842.       }
  843.       break;
  844.     case 'K' : // <esc>[0K  Erase Line
  845.       switch (parameter[0]) {
  846.         case 0 : // <esc>[0K  To end
  847.           GetCursorRow().Scroll(this,
  848.                             canvas, cursorRow, cursorColumn, INT_MAX, INT_MAX);
  849.           break;
  850.         case 1 : // <esc>[1K  To beginning
  851.           GetCursorRow().Scroll(this,
  852.                                 canvas, cursorRow, 0, cursorColumn+1, INT_MAX);
  853.           break;
  854.         case 2 : // <esc>[2K  Whole
  855.           GetCursorRow().Scroll(this, canvas, cursorRow, 0, INT_MAX, INT_MAX);
  856.           break;
  857.       }
  858.       break;
  859.     case 'L' : // <esc>[1L  Insert Lines
  860.       ScrollLines(canvas, cursorRow, scrollBottom, -PMAX(parameter[0], 1));
  861.       break;
  862.     case 'M' :
  863.       if (inParameter) // <esc>[1M  Delete Lines
  864.         ScrollLines(canvas, cursorRow, scrollBottom, PMAX(parameter[0], 1));
  865.       else // <esc>M  Scroll Backward
  866.         ScrollLines(canvas, scrollTop, scrollBottom, -1);
  867.       break;
  868.     case '@' : // <esc>[1@  Insert Character
  869.       GetCursorRow().Scroll(this,
  870.              canvas, cursorRow, cursorColumn, INT_MAX, -PMAX(parameter[0], 1));
  871.       break;
  872.     case 'P' : // <esc>[1P  Delete Character
  873.       GetCursorRow().Scroll(this,
  874.               canvas, cursorRow, cursorColumn, INT_MAX, PMAX(parameter[0], 1));
  875.       break;
  876.     case '7' :
  877.       if (inParameter)
  878.         break;
  879.       // Else is <esc>7  Save Cursor Position
  880.     case 's' : // <esc>[s  Save Cursor Position
  881.       savedCursorRow = cursorRow;
  882.       savedCursorColumn = cursorColumn;
  883.       break;
  884.     case '8' :
  885.       if (inParameter)
  886.         break;
  887.       // Else is <esc>8  Restore Cursor Position
  888.     case 'u' : // <esc>[u  Restore Cursor Position
  889.       cursorRow = savedCursorRow;
  890.       cursorColumn = savedCursorColumn;
  891.       MoveCaretPosition(canvas, 0, 0);
  892.       break;
  893.     case 'm' : { // <esc>[0m  Set Graphic Rendition
  894.       for (register PINDEX i = 0; i <= numParameters; i++) {
  895.         switch (parameter[i]) {
  896.           case 0 :
  897.             attribute.bold = FALSE;
  898.             attribute.underline = FALSE;
  899.             attribute.blink = FALSE;
  900.             attribute.inverse = FALSE;
  901.             attribute.fgColor = 8; // Window fg colour
  902.             attribute.bkColor = 9; // Window bk colour
  903.             break;
  904.           case 1 :
  905.             attribute.bold = TRUE;
  906.             break;
  907.           case 4 :
  908.             attribute.underline = TRUE;
  909.             break;
  910.           case 5 :
  911.             attribute.blink = TRUE;
  912.             break;
  913.           case 7 :
  914.             attribute.inverse = TRUE;
  915.             break;
  916.           default :
  917.             if (parameter[i] >= 30 && parameter[i] <= 39)
  918.               attribute.fgColor = parameter[i]-30;
  919.             else if (parameter[i] >= 40 && parameter[i] <= 49)
  920.               attribute.bkColor = parameter[i]-40;
  921.         }
  922.       }
  923.       break;
  924.     }
  925.     case 'n' : // <esc>[n  Device Status Report
  926.       if (keyboard != NULL) {
  927.         *keyboard << "33[" << (cursorRow+1) << ';' << (cursorColumn+1) << 'R';
  928.         keyboard->flush();
  929.       }
  930.       break;
  931.   }
  932.   inEscapeSequence = FALSE;
  933. }
  934. ///////////////////////////////////////////////////////////////////////////////
  935. PTerminalChannel::PTerminalChannel()
  936. {
  937.   terminal = NULL;
  938. }
  939. PTerminalChannel::PTerminalChannel(PTerminal & term)
  940. {
  941.   Open(term);
  942. }
  943. PTerminalChannel::~PTerminalChannel()
  944. {
  945.   Close();
  946. }
  947. BOOL PTerminalChannel::IsOpen() const
  948. {
  949.   return terminal != NULL;
  950. }
  951. PString PTerminalChannel::GetName() const
  952. {
  953.   if (IsOpen())
  954.     return terminal->GetClass();
  955.   return PString();
  956. }
  957. BOOL PTerminalChannel::Read(void * buf, PINDEX len)
  958. {
  959.   if (!IsOpen()) {
  960.     lastReadCount = 0;
  961.     lastError = NotOpen;
  962.     return FALSE;
  963.   }
  964.   readBuf.read((char *)buf, len);
  965.   lastReadCount = readBuf.gcount();
  966.   return lastReadCount > 0;
  967. }
  968. BOOL PTerminalChannel::Write(const void * buf, PINDEX len)
  969. {
  970.   if (!IsOpen()) {
  971.     lastWriteCount = 0;
  972.     lastError = NotOpen;
  973.     return FALSE;
  974.   }
  975.   terminal->Write((const char *)buf, len);
  976.   lastWriteCount = len;
  977.   return TRUE;
  978. }
  979. BOOL PTerminalChannel::Close()
  980. {
  981.   if (!IsOpen()) {
  982.     lastError = NotOpen;
  983.     return FALSE;
  984.   }
  985.   flush();
  986.   terminal->SetKeyboardStream(NULL);
  987.   terminal = NULL;
  988.   return TRUE;
  989. }
  990. BOOL PTerminalChannel::Open(PTerminal & term)
  991. {
  992.   terminal = &term;
  993.   term.SetKeyboardStream(&readBuf);
  994.   lastError = NoError;
  995.   return TRUE;
  996. }
  997. #undef new
  998. // End Of File ///////////////////////////////////////////////////////////////