[Back to TEXTEDIT SWAG index]  [Back to Main SWAG index]  [Original]


Unit MPEditor;  { VERSION 1.0  May 14, 1994 }
{  A general full-screen text editor that should compile under varying
   versions of Borland's Turbo Pascal, relying solely on the CRT
   (and System) units.

 (c) 1994, Michael Perry / Progressive Computer Services, Inc.
            All rights reserved worldwide.

  LICENSE:  This code is NOT Public Domain; It can be freely
            distributed as long as it is not modified in any
            form.  If you incorporate any part of the code into
            a program, please credit Mike Perry in the program;
            No other remuneration or consideration necessary,
            although it would be nice to get a "thank you" postcard.
            If you have suggestions or enhancements (please do)
            send them to me and I'll make sure that you get
            credit and that this unit will be continually updated.

    Author: Mike Perry / Progressive Computer Services, Inc.
            PO Box 7638, Metairie, LA 70010 USA
            BBS: (504) 835-0085  FAX: (504) 834-2160
            Fidonet: 1:396/21  Cserve: 71127,2105  AOL: Arpegi0
            Internet: 71127.2105@compuserve.com    PAN: MIKEPERRY
            Unison: MIKEP

  USAGE:    MPEDITOR manipulates text in an allocated area of memory
            specified by the TXT^ pointer.  To incorporate this unit
            into your program, simply copy your text into the array of
            byte specified by TXT^ and call the appropriate procedures.
            Supported commands:
              Arrow keys for cursor movement, INS, DEL, PgUp, PgDn
              CTRL-(left/right) moves cursor to next word;
              ^Y = delete line,
              ^B = reformat text from cursor to end of paragraph
              Ctrl-PgUp = go to top of file
              Ctrl-PgDn = go to end of file
  NOTES:
  % Statements in the unit which are commented out pertain to features
    and options which are either for demonstration purposes or have
    yet to be implemented; look for updates soon.
  % This editor unit assumes that linefeed characters (#10) are
    stripped from the input prior to editing.  If you import
    data to be edited, make sure you convert CR+LF to CR!!
  % The following routines are critical to performance and if
    implemented in ASM would improve efficiency of the unit:
    GET_LINE_DATA, SET_POSITION, SET_CURSOR
    If you can help, contact me at the addresses at the top.
}
{$R-}   { range checking off - causes error when referencing buffer array }

{ If you want to implement your own screen i/o routines, look for the
  USEQWIK directive which identifies areas in the program where you can
  make modifications to suit your objective }
{-$DEFINE USEQWIK} { implements FAST direct screen writing / requires }
                   { Eagle Performance Software's QWIK screen units }
                   { available as Shareware }
INTERFACE

{$IFDEF USEQWIK}
  USES     CRT,Qwik;
{$ELSE}
  USES     CRT;
{$ENDIF}

CONST
  TEXTMAX:WORD= 40000; { Maximum size of text editing buffer in bytes }
  CR       = 13;       { ordinal value used to indicate a new line }
  SPACE    = 32;       { ordinal value used to indicate a space }
  REFORMAT = 2;        { ordinal value to initiate reformat command / CTRL-B }
  TABSIZE  = 5;        { tab stop every five characters }

TYPE
  TXT_TYPE  = ARRAY [1..1] OF BYTE;

VAR
  TXT       : ^TXT_TYPE;    { TEXT BUFFER, pointer to memory block }

  { operational status variables ------(set during operation)------------- }
  TEXTSIZE  : LONGINT;      { size of txt array in use / max current index }
  POSITION  : LONGINT;      { index of current (cursor) position in TXT array }
  WINTOP    : LONGINT;      { index position in buffer of top of text window }
  XLINE     : BOOLEAN;      { TRUE if cursor position outside of data area }
  INSERTON  : BOOLEAN;      { TRUE indicates insert mode on }
  VROW,
  VCOLUMN   : BYTE;         { VIRTUAL ROW/COLUMN position within editing area }
  WIDTH,
  HEIGHT    : BYTE;         { width and height of current editing window }
  SCRBUMP   : BYTE;         { chars to bump over display / not to exceed
WIDTH! }
  OFFSET    : LONGINT;      { screen display offset, column position to begin
displaying }
{  MARKSTART,
   MARKEND  : LONGINT;      { start/end index of marked text / NOT IMPLEMENTED
}
  { operational configuration vars ----(set by user)----------------------- }
  MARKATTR,                 { marked text attribute }
  BACKATTR,                 { background text window attribute }
  NORMATTR,                 { attribute values for normal & hilighted text }
  BORDATTR  : WORD;         { attribute value for border }
  R1,C1,                    { row,column of upper left coord of edit window }
  R2,C2     : BYTE;         { row,column of lower right coord on edit win }
  MAXCOLUMN : LONGINT;      { maximum line length/column size, 0=No Limit }
{  MAXROW    : LONGINT;      { maximum number of lines allowed, 0=No Limit }

  { prototypes ------------------------------------------------------------ }

   {FUNCTION GETINPUT(VAR FUNCTIONKEY:BOOLEAN):BYTE; NEAR }
   {FUNCTION SPACES(COUNT:BYTE):STRING;              NEAR }
   {PROCEDURE CLEAR_TXT;                             NEAR }
  FUNCTION GETTEXTEND:LONGINT;
  FUNCTION INITIALIZE_TXT(VAR PTXT:POINTER;SIZE:LONGINT):BOOLEAN;
  PROCEDURE DISPOSE_TXT(VAR PTXT:POINTER;SIZE:LONGINT);
   {PROCEDURE DRAW_BOX(R1,C1,R2,C2:BYTE);            NEAR }
   {PROCEDURE INITIALIZE_WINDOW(R1,C1,R2,C2:BYTE);   NEAR }
   {PROCEDURE CLEAR_WINDOW;                          NEAR }
   {PROCEDURE CLEAR_LINE;                            NEAR }
   {PROCEDURE BUMP_TXT(COUNT:LONGINT);               NEAR }
   {PROCEDURE DEL_CHARS(COUNT:LONGINT);              NEAR }
   {PROCEDURE GET_LINE_DATA(POS:LONGINT; VAR STARTINDEX,ENDINDEX,COL:LONGINT);}
   {PROCEDURE STUFF_TXT(s:string);                   NEAR }
  FUNCTION WINBOTTOM:LONGINT;
   {PROCEDURE SHOW_LINE;                             NEAR }
  PROCEDURE SHOW_TXT;
  PROCEDURE DISPLAY_TXT(VAR PT:POINTER);
  PROCEDURE SCROLLUP(LINES:BYTE);
  PROCEDURE SCROLLDOWN(LINES:BYTE);
   {PROCEDURE SET_POSITION;                          NEAR }
   {PROCEDURE SET_CURSOR;                            NEAR }
   {PROCEDURE WORD_WRAP(startpoint,endpoint,length:LONGINT);}
   {FUNCTION LINEUP:LONGINT;                         NEAR }
   {FUNCTION LINEDOWN:LONGINT;                       NEAR }
  PROCEDURE READ_TXT(VAR PT:POINTER;FILENAME:STRING;VAR TEXTSIZE:LONGINT);
   {PROCEDURE DIRECTION(C:BYTE);                     NEAR }
   {FUNCTION PARSE_INPUT:BYTE;                       NEAR }
  PROCEDURE SETUP_TEXT_SETTINGS(Row1,Column1,Row2,Column2:BYTE;DRAWBOX:BOOLEAN) ;
  PROCEDURE EDIT(PT:POINTER;VAR RETURNCODE:BYTE);

IMPLEMENTATION

(***************************************************************************)
FUNCTION GETINPUT(VAR FUNCTIONKEY:BOOLEAN):BYTE;
{ read keyboard and return character/function key }
VAR CH: CHAR;
BEGIN
  CH:=ReadKey;
  IF (CH=#0) THEN
    BEGIN
      CH:=ReadKey;
      FUNCTIONKEY:=TRUE;
    END
  ELSE FUNCTIONKEY:=FALSE;
  GETINPUT:=ORD(CH);
END;
(***************************************************************************)
FUNCTION SPACES(COUNT:BYTE):STRING;
{ returns COUNT number of spaces }
{ NOTE: Unpredictable results if count exceeds 255!! }
var temp:string;
BEGIN
  TEMP:='';
  WHILE COUNT>0 DO BEGIN
    TEMP:=TEMP+#32;
    DEC(COUNT);
  END;
  SPACES:=TEMP;
END;
(***************************************************************************)
PROCEDURE CLEAR_TXT;
{ zeros the text array & associated values }
BEGIN
  fillchar(txt^,TEXTMAX,0);
  textsize:=0;
  position:=1;
END;
(***************************************************************************)
FUNCTION GETTEXTEND:LONGINT;
{ find the end of text by looking for null character
  %% This is a technique that I use to identify the actual size of
     a text buffer; if you're reading data from a disk structure,
     unless you save the actual size, you'll need to determine it.
     I make sure any unused area in my text buffer is padded with
     nuls }
var I:longint;
BEGIN
  FOR I:=1 TO TEXTMAX DO
    IF TXT^[I]=0 THEN BEGIN
      GETTEXTEND:=I-1;
      EXIT;
    END;
  GETTEXTEND:=TEXTSIZE;
END;
(***************************************************************************)
FUNCTION INITIALIZE_TXT(VAR PTXT:POINTER;SIZE:LONGINT):BOOLEAN;
{ create/allocate memory for text buffer }
BEGIN
  if MaxAvail < SIZE then
    INITIALIZE_TXT:=FALSE     { not enough available memory }
  else BEGIN
    GETMEM(PTXT,SIZE);
    INITIALIZE_TXT:=TRUE;
    TEXTMAX:=SIZE;            { set max size of text }
    TXT:=PTXT;                { establish pointer for routines }
    CLEAR_TXT;                { zero text }
  END;
END;

(***************************************************************************)
PROCEDURE DISPOSE_TXT(VAR PTXT:POINTER;SIZE:LONGINT);
{ disposes text buffer }
BEGIN
  FREEMEM(PTXT,SIZE);
END;
(***************************************************************************)
PROCEDURE DRAW_BOX(R1,C1,R2,C2:BYTE);
{ surrounds the specified area with a box }
{ NOTE! There are no checks to make sure the box area isn't off the screen!
        and this (and other) routines must be slightly modified if you want
        the text area to fill up the entire screen due to anomolies with
        TP's WINDOW function }
var I:byte;
BEGIN
{$IFDEF USEQWIK}
  { draw horizontal line }
  FOR I:=(C1-1) TO (C2+1) DO BEGIN
    qwrite(R1-1,I,BORDATTR,'Ä');
    qwrite(R2+1,I,BORDATTR,'Ä');
  END;
  { draw vertical line }
  FOR I:=(R1-1) TO (R2+1) DO BEGIN
    QWRITE(I,C1-1,BORDATTR,'³');
    QWRITE(I,C2+1,BORDATTR,'³');
  END;
  QWRITE(R1-1,C1-1,BORDATTR,'Ú');
  QWRITE(R2+1,C1-1,BORDATTR,'À');
  QWRITE(R1-1,C2+1,BORDATTR,'¿');
  QWRITE(R2+1,C2+1,BORDATTR,'Ù');
{$ELSE}
  TEXTATTR:=BORDATTR;
  { draw horizontal line }
  FOR I:=(C1-1) TO (C2+1) DO BEGIN
    GOTOXY(I,R1-1); WRITE('Ä');
    GOTOXY(I,R2+1); WRITE('Ä');
  END;
  { draw vertical line }
  FOR I:=(R1-1) TO (R2+1) DO BEGIN
    GOTOXY(C1-1,I); WRITE('³');
    GOTOXY(C2+1,I); WRITE('³');
  END;
  GOTOXY(c1-1,r1-1); WRITE('Ú');
  GOTOXY(c1-1,r2+1); WRITE('À');
  GOTOXY(c2+1,r1-1); WRITE('¿');
  GOTOXY(c2+1,r2+1); WRITE('Ù');
  TEXTATTR:=NORMATTR;
{$ENDIF}
END;
(***************************************************************************)
PROCEDURE INITIALIZE_WINDOW(R1,C1,R2,C2:BYTE);
{ defines text window and clears screen }
BEGIN
{$IFNDEF USEQWIK}
  WINDOW(C1,R1,C2+1,R2);
{$ENDIF}
END;
(***************************************************************************)
PROCEDURE CLEAR_WINDOW;
{ clears the current text window }
BEGIN
{$IFDEF USEQWIK}
  QFILL(r1,c1,HEIGHT,WIDTH,BACKATTR,#32);
{$ELSE}
  textattr:=backattr;
{ since TP forces an unwanted scroll when writing to the lower right corner
  of a window, we create a window 1-column larger and init a smaller one when
  we want to clear the screen, there is an extra column defined in the
  working window so that unwanted scrolls are not accomplished }
  WINDOW(C1,R1,C2,R2);
  CLRSCR;
  WINDOW(C1,R1,C2+1,R2);
{$ENDIF}
END;
(***************************************************************************)
PROCEDURE CLEAR_LINE;
{ clears the current line }
BEGIN
{$IFDEF USEQWIK}
  QFILL(R1+VROW-1,C1,1,WIDTH,BACKATTR,#32);
  { FYI, the arguments for QFILL are:
     QFILL(row,column,rows,columns,attribute,char);  }
{$ELSE}
  TEXTATTR:=BACKATTR;
  WINDOW(C1,R1,C2,R2);
  gotoxy(1,vrow); clreol;
  WINDOW(C1,R1,C2+1,R2);
{$ENDIF}
END;
(***************************************************************************)
PROCEDURE BUMP_TXT(COUNT:LONGINT);
{ moves text at POSITION index over COUNT bytes, for inserting data }
{ this procedure does NOT change, position or cursor indexes }
VAR I:LONGINT;
BEGIN
  inc(textsize,COUNT);
  for i:=textsize downto position do { move everything forward 1 }
    txt^[i]:=txt^[i-COUNT];
END;
(***************************************************************************)
PROCEDURE DEL_CHARS(COUNT:LONGINT);
{ erase COUNT chars at position, shorten text array }
var I:longint;
BEGIN
  FOR I:=POSITION TO (TEXTSIZE-1) DO
    TXT^[I]:=TXT^[I+COUNT];
  DEC(TEXTSIZE,COUNT);
END;

(***************************************************************************)
PROCEDURE GET_LINE_DATA(POS:LONGINT; VAR STARTINDEX,ENDINDEX,COL:LONGINT);
{ given the array index (position), calculate the start & ending index of the
  current line, also returning VIRTUAL column position on the current line

  procedure returns:  1,1,1 if at the top of the file;
  procedure returns:  textsize,textsize,1 at bottom of file

  This is one procedure, that if implemented in ASM would improve the
  overall performance of this unit (I'm open to suggestions).
}
VAR i:longint;
BEGIN
  startindex:=0; endindex:=0; col:=0;
  if pos<1 then exit;               { invalid position }
  if pos>textsize then begin        { at end of text }
    endindex:=textsize+1;
  end else begin
    for i:=pos to textsize do       { find end of line index }
      if txt^[i]=CR then begin      { found CR at end of line }
        endindex:=i;
        i:=textsize;                { force end of loop }
      end;
    if endindex=0 then endindex:=textsize+1;  { last line obviously }
  end;
                                    { find beginning of line index }
  for i:=(endindex-1) downto 1 do   { FOR checks=endvalue, if not increments! }
    if txt^[i]=CR then begin        { found CR at beginning of line }
      startindex:=i+1;              { index of previous CR+1 }
      i:=1;                         { force end of loop }
    end;
  if startindex=0 then startindex:=1;  { begin of line is top of text }

  col:=pos-startindex+1;            { calculate VIRTUAL column position }
END;
(***************************************************************************)
PROCEDURE STUFF_TXT(s:string);
{ add string/char to txt array, bump POSITION up one }
VAR j,b1,e1,col1:longint; t:byte;
BEGIN
  t:=length(s);
  if ((inserton) or (txt^[position]=CR)) and ((textsize+t)>textmax) then
begin  { no more room }
    write(#7); exit;
  end;

{ if xline, and text added, make sure to bump position to the end of line }
  IF (XLINE) THEN BEGIN { pad the short line with spaces to the position }
    GET_LINE_DATA(POSITION, b1,e1,col1);
    j:=(offset+vcolumn)-(e1-b1+1);  { number of spaces to pad }
    IF ((textsize+t+j)>textmax) then BEGIN { check for avail space }
      write(#7); exit;
    END;
    bump_txt(j);
    for b1:=position to (position+j-1) do
      txt^[b1]:=SPACE;
    XLINE:=FALSE;
    POSITION:=POSITION+J;
  END;

  if (inserton) OR (txt^[position]=CR) then
    if (position<=textsize) then begin { insert }
      bump_txt(t);
      FOR J:=1 TO T DO BEGIN
        txt^[position]:=ORD(S[J]);  { add/replace character }
        INC(POSITION);              { move pointer up one }
        INC(VCOLUMN);
      END;
      exit;
    end else begin                  { append / position > textsize }
      FOR J:=1 TO T DO BEGIN
        inc(textsize);
        txt^[textsize]:=ORD(S[J]);
        INC(VCOLUMN);
      END;
      position:=textsize+1;         { position pointer at end of text }
    end
  else if position<=textsize then begin { overwrite }
    FOR J:=1 TO T DO BEGIN
      txt^[position]:=ORD(S[J]);        { overwrite current position }
      inc(position);                    { move pointer one over }
    END;
    INC(VCOLUMN,T);
  end else begin { append }
    if ((textsize+T)>=textmax) then begin  { can't append if buffer full }
      write(#7);
      exit;
    end;
    FOR J:=1 TO T DO BEGIN
      inc(textsize);
      txt^[textsize]:=ORD(S[J]);
    END;
    position:=textsize+1;
    INC(VCOLUMN,T);
  end;
END;
(***************************************************************************)
FUNCTION WINBOTTOM:LONGINT;
{ returns the text array index value of the last character in the text window }
var i:longint; linecount:byte;
BEGIN
  LINECOUNT:=0;
  FOR I:=WINTOP TO TEXTSIZE DO BEGIN
    IF TXT^[I]=CR THEN INC(LINECOUNT);
    IF LINECOUNT=HEIGHT THEN BEGIN { found last line in text window }
      WINBOTTOM:=I;
      EXIT;
    END;
  END; { for loop thru text }
  WINBOTTOM:=TEXTSIZE; { end before last text line found, so text ends in
window }
END;

(***************************************************************************)
PROCEDURE SHOW_LINE;
{ rewrites the current line to the window }
VAR I,b1,e1,col1:longint; S:STRING;
BEGIN
  GET_LINE_DATA(position, b1,e1,col1);
  IF (B1>TEXTSIZE) THEN BEGIN CLEAR_LINE; EXIT; END; { nothing there }
  col1:=(offset+b1+width-1); if col1>textsize then col1:=textsize; {eol pos}
  S:='';
  for i:=(offset+b1) to col1 {(offset+b1+width-1)} do begin
    if txt^[i]=CR then i:=col1 {(offset+b1+width-1)} { force end }
    else begin
      s:=s+chr(txt^[i]);
    end;
  end;
  CLEAR_LINE;
{$ifdef USEQWIK}
  QWRITE(R1+VROW-1,C1,NORMATTR,S);
  GOTORC(R1+VROW-1,C1+VCOLUMN-1);
{$ELSE}
  TEXTATTR:=NORMATTR;
  GOTOXY(1,VROW);  WRITE(S);
  GOTOXY(VCOLUMN,VROW);
{$ENDIF}
END;
(***************************************************************************)
PROCEDURE SHOW_TXT;
{ display text to screen area
  sets VROW and VCOLUMN to match displayed area where position
  is and moves cursor to that location }
var I,R,C,CWIDTH:LONGINT;
BEGIN
  R:=1; C:=1;  { set start row/column }
  CWIDTH:=0;
  CLEAR_WINDOW;

  { % hide cursor }
  FOR I:=WINTOP TO TEXTSIZE DO BEGIN
    IF (R>HEIGHT) OR (I>TEXTSIZE) THEN begin { check for outside vertical
boundaries OR end }
      {$IFDEF USEQWIK}
        GOTORC(R1+VROW-1,C1+VCOLUMN-1);
      {$ELSE}
        GOTOXY(VCOLUMN,VROW);
      {$ENDIF}
      EXIT;  { done, filled window }
    END;
    IF (TXT^[I]=CR) THEN BEGIN   { -------------- check for carriage return }
      INC(R);   { bump row down }
      CWIDTH:=0;
      C:=1;
{ % IF TXT^[I+1]=10 THEN INC(I);  { check for additional LF / skip over }

    END ELSE BEGIN   { ----------------------------------- printable char }
      INC(CWIDTH);
      IF CWIDTH>OFFSET THEN       { if screen offset in effect }
        IF C<= WIDTH THEN BEGIN    { if line not off the screen }
{$IFDEF USEQWIK}
          QWRITE(R1+R-1,C1+C-1,NORMATTR,CHR(TXT^[I]));
{$ELSE}
          GOTOXY(C,R); textattr:=normattr;
          WRITE(CHR(TXT^[I]));
{$ENDIF}
          INC(C);
        END else INC(C);  { increment column counter anyway even though not
printed }
     END;
     IF I>TEXTSIZE THEN I:=TEXTSIZE;  { if bumped past, set to end loop }
  END;  { FOR loop }
{$IFDEF USEQWIK}
  GOTORC(R1+VROW-1,C1+VCOLUMN-1);
{$ELSE}
  GOTOXY(VCOLUMN,VROW);
{$ENDIF}
  EXIT;
END;
(***************************************************************************)
PROCEDURE DISPLAY_TXT(VAR PT:POINTER);
{ display text, specified by pointer
  can be used by an external viewing routine }
VAR TEMP:POINTER;
BEGIN
  TEMP:=addr(TXT);
  TXT:=PT;
  SHOW_TXT;
  TXT:=TEMP;
END;
(***************************************************************************)
PROCEDURE SCROLLUP(LINES:BYTE);
{ scroll screen up x lines; does not change cursor or text pointer }
var i,b1,e1,col1,LINECOUNT:longint;
BEGIN
  LINECOUNT:=0;
  FOR I:=WINTOP DOWNTO 1 DO BEGIN
    if txt^[i]=CR then begin  { found end of prev line }
       INC(LINECOUNT);
       IF (LINECOUNT=LINES) THEN BEGIN
         GET_LINE_DATA(I, b1,e1,col1);
         WINTOP:=B1;
         EXIT;
       END;
    END; { cr found }
  END; { for loop }
  WINTOP:=1;
END;
(***************************************************************************)
PROCEDURE SCROLLDOWN(LINES:BYTE);
{ scroll screen down x lines, does not change cursor or text pointer }
var i,b1,e1,col1,LINECOUNT:longint;
BEGIN
  linecount:=0;
  for i:=WINTOP to textsize do begin
    if txt^[i]=CR then begin
      { i=index pos of CR of next line }
      inc(linecount);
      if (linecount=lines) {or (i=textsize)} then begin
        WINTOP:=i+1;
        SHOW_TXT;
        EXIT;
      end;  { found specified number of lines }
    end; { if CR found }
  end; { loop thru text }
END;

(***************************************************************************)
PROCEDURE SET_POSITION;
{ sets POSITION index based on cursor location and WINTOP index }
var I,b1,e1,col1,R,LINECOUNT:longint;
BEGIN
  R:=1; LINECOUNT:=1;
  FOR I:=WINTOP TO TEXTSIZE DO BEGIN
    if (VROW=R) then begin  { line cursor on found }
      GET_LINE_DATA(I, b1,e1,col1);
      IF ((E1-B1+1) < (VCOLUMN+OFFSET)) THEN BEGIN
        POSITION:=E1;
        XLINE:=TRUE;
      END ELSE BEGIN
        POSITION:=B1+VCOLUMN-1;
        XLINE:=FALSE;
      END;
      EXIT;
    end; { cursor line found }
    if txt^[i]=CR then begin
      INC(R);
      INC(LINECOUNT);
    end;
  END; { for loop thru text }
  POSITION:=TEXTSIZE+1;  { assuming cursor at end of text then }
  VROW:=LINECOUNT;  { not sure if this should be here, but takes care
                      of case where scrolling causes most of screen to
                      be past the end of file (where the cursor pos is)
                    }
  GET_LINE_DATA(POSITION, b1,e1,col1);
  IF (VCOLUMN>COL1) THEN XLINE:=TRUE ELSE BEGIN
    POSITION:=B1+VCOLUMN+OFFSET-1;
    XLINE:=FALSE;
  END;
END;
(***************************************************************************)
PROCEDURE SET_CURSOR;
{ finds position and sets VCOLUMN & VROW and OFFSET appropriately in window }
{ ALWAYS sets XLINE to FALSE }
var i,b1,e1,col1,R,C:longint; screenchanged:BOOLEAN;
BEGIN
  R:=1; C:=1; SCREENCHANGED:=FALSE; XLINE:=FALSE;
  FOR I:=WINTOP TO TEXTSIZE+1 DO BEGIN
    IF I=POSITION THEN BEGIN   { found it ------------------------ }
      if (c>offset) and (c<=(offset+width)) then begin  { in window }
        dec(c,offset);
      end else BEGIN
        SCREENCHANGED:=TRUE;
        IF (C<=WIDTH) THEN BEGIN
          OFFSET:=0;
        END ELSE BEGIN
          OFFSET:=(C-WIDTH)+SCRBUMP;
          C:=WIDTH-SCRBUMP;
        END;
      END;
      VCOLUMN:=C;
      VROW:=R;
      IF (SCREENCHANGED) THEN SHOW_TXT else
{$IFDEF USEQWIK}
        GOTORC(R1+VROW-1,C1+VCOLUMN-1);
{$ELSE}
        GOTOXY(VCOLUMN,VROW);
{$ENDIF}
      EXIT;
    END; { position found }
  IF TXT^[I]=CR THEN BEGIN
    INC(R); C:=1;
  END ELSE INC(C);
  IF (R>HEIGHT) {OR (R<1)} THEN BEGIN
    GET_LINE_DATA(WINTOP, b1,e1,col1);
    WINTOP:=E1+1;
    R:=HEIGHT;
    SCREENCHANGED:=TRUE;
  END;
 END; { for }
END;
(***************************************************************************)
PROCEDURE WORD_WRAP(startpoint,endpoint,length:LONGINT);
{ word wrap a section of text }
var ccount,i,spacepos,lastcr:longint; showit:boolean;
BEGIN
  IF LENGTH=0 THEN EXIT;  { no length specified so get outta here }
  SPACEPOS:=0; SHOWIT:=FALSE; CCOUNT:=0; LASTCR:=-1;
  FOR I:=STARTPOINT TO (ENDPOINT-1) DO BEGIN
    INC(CCOUNT);
    IF TXT^[I]=SPACE THEN SPACEPOS:=I;
    IF TXT^[I]=CR THEN    { end wrap when to CRs follow, otherwise -> space }
      IF LASTCR=(I-1) THEN BEGIN
        TXT^[I-1]:=CR; { restore prev CR }
        SET_CURSOR;
        SHOW_TXT;
        EXIT;
      END ELSE BEGIN
        TXT^[I]:=SPACE;
        SPACEPOS:=I;
        LASTCR:=I;
      END;
    IF (CCOUNT)>LENGTH THEN BEGIN  { past point }
      IF SPACEPOS=0 THEN BEGIN     { force a CR }
        SPACEPOS:=POSITION; { save pos }
        POSITION:=I;
        BUMP_TXT(1);  { insert 1 byte at position }
        INC(ENDPOINT);
        POSITION:=SPACEPOS; { restore pos }
        TXT^[I]:=CR;
        CCOUNT:=0;
      END ELSE BEGIN
        TXT^[SPACEPOS]:=CR; { turn last space into a CR }
        CCOUNT:=I-SPACEPOS; { calc next line len w/wrap }
      END;
      SHOWIT:=TRUE;
    END; { line past length }
  END; { for }
  IF SHOWIT THEN BEGIN
    SET_CURSOR;
    SHOW_TXT;
  END;
END;

(***************************************************************************)
FUNCTION LINEUP:LONGINT;
{ returns new index in file, one line up }
{ MOVES cursor on screen as well }
var b1,b2,e1,e2, col1,col2,len1,len2:longint;
BEGIN
  GET_LINE_DATA(position, b1,e1,col1); { get data on current line }
  len1:=e1-b1+1;                       { length of line + CR }
  if b1=1 then BEGIN                   { check for top of text }
    LINEUP:=POSITION; EXIT;
  END;
  GET_LINE_DATA(B1-1,     b2,e2,col2); { get data on previous line }
  len2:=e2-b2+1;
  IF (XLINE) THEN BEGIN
    col2:=b2+vcolumn+offset-1; { in case of move to non-xline, set position }
  END ELSE
    col2:=b2+col1-1;  { index position of one line up, tentative }

  if col2<1 then col2:=1 else { top of file }
    if (col2>e2) then begin   { previous line shorter than current line }
      col2:=e2;               { make one line up, end of previous line }
      XLINE:=TRUE;
    end else begin
      XLINE:=FALSE;
    end;
  LINEUP:=COL2;

  IF (WINTOP>col2) THEN BEGIN  { scroll the screen up }
    WINTOP:=B2;
    SHOW_TXT;
  END ELSE DEC(VROW);  { bump cursor up }
{$IFDEF USEQWIK}
  GOTORC(R1+VROW-1,C1+VCOLUMN-1);
{$ELSE}
  GOTOXY(VCOLUMN,VROW);
{$ENDIF}
END;
(***************************************************************************)
FUNCTION LINEDOWN:LONGINT;
{ returns new index in file, one line down }
{ MOVES cursor on screen as well }
var b1,b2,e1,e2, col1,col2,len1,len2:longint;
BEGIN
  GET_LINE_DATA(position, b1,e1,col1); { get data on current line }
  len1:=e1-b1+1;                       { calc length of line incl. CR }
  if e1>=textsize then begin           { can't go down on last line }
    LINEDOWN:=POSITION; EXIT;
  end;
  GET_LINE_DATA(e1+1,     b2,e2,col2); { get data on next line }
  len2:=e2-b2+1;

  IF (XLINE) THEN BEGIN
    col2:=b2+vcolumn+offset-1;   { in case of move to non-xline, set position }
  END ELSE
    col2:=b2+col1-1;   { index position of one line down, tentative }

  if (col2>e2) then begin  { next line position is past end of next line }
      col2:=e2;            { make one line down, end of next line }
      xline:=TRUE;
    end else begin
      xline:=FALSE;
  end;
  LINEDOWN:=COL2;

  IF (VROW=HEIGHT) THEN BEGIN   { down off screen, scroll text up }
    SCROLLDOWN(1);  {WINTOP:=B2;}
  END ELSE INC(VROW);  { bump screen down }
{$IFDEF USEQWIK}
  GOTORC(R1+VROW-1,C1+VCOLUMN-1);
{$ELSE}
  GOTOXY(VCOLUMN,VROW);
{$ENDIF}
END;
(***************************************************************************)
PROCEDURE READ_TXT(VAR PT:POINTER;FILENAME:STRING;VAR TEXTSIZE:LONGINT);
{ reads text from file into buffer, strips LFs }
{ HORRIBLY SLOW, but not intended to be a real part of this unit }
VAR
    F    :FILE OF BYTE;
    PTXT :^TXT_TYPE;
BEGIN
  PTXT:=PT;
  ASSIGN(F,FILENAME);
  RESET(F);
  TEXTSIZE:=0;
  IF IORESULT<>0 THEN EXIT;
  IF EOF(F) THEN BEGIN CLOSE(F); EXIT; END;
  WHILE NOT(EOF(F)) DO BEGIN
    INC(TEXTSIZE);
    READ(F,PTXT^[TEXTSIZE]);
    if (Ptxt^[textsize]=10) then begin
      dec(textsize);  { remove LFs }
    end;
    IF TEXTSIZE>=TEXTMAX THEN BEGIN
      CLOSE(F);
      EXIT;
    END;
  END;
  CLOSE(F);
END;

(***************************************************************************)
PROCEDURE DIRECTION(C:BYTE);
{ act on direction keys }
var b1,e1,col1:longint; T:BYTE;
BEGIN
      case C of
       72:begin  { up }
           POSITION:=LINEUP;
         end;
       80:begin  { down }
            POSITION:=LINEDOWN;
          end;
       75:begin  { left }
            if position=1 then begin
              write(#7);
              exit;
            end;
            if (xline) then begin
              dec(vcolumn);
              { check to see if moved onto text }
              get_line_data(position,b1,e1,col1);
              if (offset+vcolumn)=(e1-b1+1) then begin
                xline:=FALSE;
              end;
            end else begin  { not xline }
              if txt^[position-1]=CR then begin      { back up one line? }
                get_line_data(position-1,b1,e1,col1);
                vcolumn:=col1;
                if col1>width then begin { left to prev line off screen }
                  offset:=col1-width+2;
                  vcolumn:=col1-offset;
                  SHOW_TXT;
                end else offset:=0;
                dec(vrow)
              end else begin
                dec(vcolumn);
              end;
              dec(position);
            end; { xline }
            if (vcolumn<1) and (offset>0) then begin
              vcolumn:=1;
              dec(offset);
              SHOW_TXT;
            end;
{$IFDEF USEQWIK}
            GOTORC(R1+VROW-1,C1+VCOLUMN-1);
{$ELSE}
            GOTOXY(VCOLUMN,VROW);
{$ENDIF}
          end;
       77:begin  { right }
            if (xline) then begin
              inc(vcolumn);
            end else begin
              inc(position);
              inc(vcolumn);
              if (txt^[position-1]=CR) OR ((position-1)>=TEXTSIZE) then begin
{ at eol }
                dec(position);
                xline:=true;
              end;
            end;

            IF (MAXCOLUMN>0) AND ((VCOLUMN+OFFSET)>MAXCOLUMN) THEN BEGIN
              GET_LINE_DATA(POSITION,b1,e1,col1);
              IF E1>=TEXTSIZE THEN BEGIN
                DEC(VCOLUMN);
              END ELSE BEGIN
                POSITION:=E1+1;
                SET_CURSOR;
                EXIT;
              END;
            END;

            if vcolumn>width then begin { moved outside window }
              inc(offset);
              dec(vcolumn);
              SHOW_TXT;
            end;
{$IFDEF USEQWIK}
            GOTORC(R1+VROW-1,C1+VCOLUMN-1);
{$ELSE}
            GOTOXY(VCOLUMN,VROW);
{$ENDIF}
          end;
       71:begin  { HOME, to beginning of current line }
            GET_LINE_DATA(POSITION,B1,E1,COL1);
            POSITION:=B1; VCOLUMN:=1;
            IF OFFSET>0 THEN BEGIN
              OFFSET:=0;
              SHOW_TXT;
            END;
{$IFDEF USEQWIK}
            GOTORC(R1+VROW-1,C1+VCOLUMN-1);
{$ELSE}
            GOTOXY(VCOLUMN,VROW);
{$ENDIF}
            XLINE:=FALSE;
          end;
       79:begin  { END, to end of current line }
            GET_LINE_DATA(POSITION,B1,E1,COL1);
            POSITION:=E1;
            { calculate offset & cursor position }
            IF (E1-(B1+OFFSET)+1)>WIDTH THEN BEGIN  { off screen }
              offset:=(e1-b1+1)-width+2; {SCRBUMP}
              vcolumn:=width-2; {SCRBUMP}
              SHOW_TXT;
            END ELSE VCOLUMN:=((E1-B1+1)-offset);
{$IFDEF USEQWIK}
            GOTORC(R1+VROW-1,C1+VCOLUMN-1);
{$ELSE}
            GOTOXY(VCOLUMN,VROW);
{$ENDIF}
          end;
       73:begin  { PGUP, up one screen }
            IF (WINTOP=1) THEN BEGIN
              POSITION:=1;
              VCOLUMN:=1; VROW:=1;
{$IFDEF USEQWIK}
              GOTORC(R1+VROW-1,C1+VCOLUMN-1);
{$ELSE}
              GOTOXY(VCOLUMN,VROW);
{$ENDIF}
            END ELSE BEGIN
              SCROLLUP(HEIGHT);
              SET_POSITION;
              SHOW_TXT;
            END;
          end;
       81:begin  { PGDN, down one screen }
            IF (WINBOTTOM=TEXTSIZE) THEN BEGIN
              POSITION:=TEXTSIZE+1;
              SET_CURSOR;
            END ELSE BEGIN
              SCROLLDOWN(HEIGHT);
              SET_POSITION;
              SHOW_TXT;
            END;
          end;
        82:BEGIN
             INSERTON:=NOT(INSERTON);     { INS toggle insert status }
{ %      IF (INSERTON) THEN GETBLOCKCURSOR ELSE GETUNDERLINECURSOR;}
           END;
        83:BEGIN                        { DEL }
             T:=ORD(TXT^[POSITION]);
             IF POSITION=TEXTSIZE+1 THEN EXIT;
             IF (XLINE) THEN BEGIN   { hitting DEL past eol, special case }
               STUFF_TXT(#0);
               DEC(POSITION);
               DEC(VCOLUMN);
               DEL_CHARS(1);
             END;
             DEL_CHARS(1);
             IF (T=CR) OR (POSITION>=TEXTSIZE) THEN SHOW_TXT ELSE SHOW_LINE;
           END;
       132:begin  { CTRL-PgUP - top of text }
             POSITION:=1; VROW:=1; VCOLUMN:=1; XLINE:=FALSE;
             IF (WINTOP=1) AND (OFFSET=0) THEN
{$IFDEF USEQWIK}
               GOTORC(R1+VROW-1,C1+VCOLUMN-1)
{$ELSE}
               GOTOXY(VCOLUMN,VROW)
{$ENDIF}
             ELSE BEGIN
               WINTOP:=1; OFFSET:=0;
               SHOW_TXT;
             END;
          end;
       115:begin  { CTRL <- }
             if position<3 then exit;
             for col1:=(position-2) downto 1 do
               if (txt^[col1]=SPACE) then begin
                 position:=col1+1;
                 if position<wintop then begin
                   wintop:=1;
 { this could be avoided if set-cursor started at 1 instead of wintop,
   but it would reduce overal performance }
                   set_cursor;
                   show_txt
                 end else
                   set_cursor;
                 exit;
               end;
           end;
       116:begin  { CTRL -> }
             if position>=textsize then exit;
             for col1:=position+1 to textsize do
               if (txt^[col1]=SPACE) then begin
                 position:=col1+1;
                 set_cursor;
                 exit;
               end;
           end;
       118:begin  { CTRL-PgDN - end of text }
             position:=textsize+1;
             SET_CURSOR;
         end;
       67:BEGIN    { F9 }
          END;
      end; { case }
end;

(***************************************************************************)
FUNCTION PARSE_INPUT:BYTE;
{ main encapsulation of editing routine, read keys and act }
var c         :byte;
    fkey      :boolean;
    leaving   :boolean;
    b1,e1,col1:longint;
{ RETURNS:
      1=ESC
      2=ALT-X
      3=F1
      4=F10
      5=F2
}
BEGIN
  LEAVING:=FALSE;
  REPEAT
    c:=getinput(fkey);

    IF (C=27) OR ((FKEY) AND (C IN [59,45,60,68])) THEN BEGIN  { exit
conditions }
      IF C=27 THEN PARSE_INPUT:=1 ELSE  { esc }
      IF C=45 THEN PARSE_INPUT:=2 ELSE  { Alt-X }
      IF C=59 THEN PARSE_INPUT:=3 ELSE  { F1 }
      IF C=68 THEN PARSE_INPUT:=4 ELSE  { F10 }
      IF C=60 THEN PARSE_INPUT:=5;      { F2 }
      EXIT;
    END ELSE
    IF (FKEY) THEN BEGIN  { ------------------ eval FNC & CURSOR keys ----- }
      DIRECTION(C);
    END { if function key pressed }
    ELSE BEGIN                      { alphanumeric key - process data }
      CASE C OF                     { check alpha keys }
       REFORMAT:BEGIN               { CTRL-B, 02, REFORMAT }
            GET_LINE_DATA(POSITION,b1,e1,col1);
            WORD_WRAP(B1,TEXTSIZE,MAXCOLUMN);
          END;
       CR:begin                     { carriage return }
            IF (INSERTON) OR (POSITION>TEXTSIZE) THEN BEGIN
              OFFSET:=0;
              INC(VROW);
              IF VROW>HEIGHT THEN BEGIN
                 SCROLLDOWN(1);
                 DEC(VROW);
               END;
               STUFF_TXT(CHR(C));
               VCOLUMN:=1;
               SHOW_TXT;
             end ELSE BEGIN  { enter pressed with overwrite on }
               GET_LINE_DATA(POSITION,B1,E1,COL1);
               POSITION:=E1+1;
               OFFSET:=0;
               SET_CURSOR;
               show_txt;
             END;
           END;
        08:IF POSITION<>1 THEN BEGIN    { backspace }
             IF (XLINE) THEN BEGIN   { can't erase dead zone }
                DEC(VCOLUMN);        { just move cursor left }
                SET_POSITION;
             END ELSE BEGIN
               DEC(POSITION);
               IF TXT^[POSITION]=CR THEN BEGIN { backspace/erase line }
                 DEL_CHARS(1);
                 SET_CURSOR;
                 SHOW_TXT;
               END ELSE BEGIN
                 DEL_CHARS(1);
                 DEC(VCOLUMN);
                 IF (VCOLUMN=0) THEN
                   IF (OFFSET>=SCRBUMP) THEN BEGIN
                     DEC(OFFSET,SCRBUMP); VCOLUMN:=SCRBUMP;
                     SHOW_TXT;
                   END ELSE BEGIN
                     OFFSET:=0; SET_CURSOR;
                     SHOW_TXT;
                   END
                 ELSE SHOW_LINE;
               END;
             END; { xline / else }
{$IFDEF USEQWIK}
             GOTORC(R1+VROW-1,C1+VCOLUMN-1);
{$ELSE}
             GOTOXY(VCOLUMN,VROW);
{$ENDIF}
           END;
         09:BEGIN                      { TAB }
              GET_LINE_DATA(POSITION, b1,e1,col1);
              col1:=tabsize-((position-b1) mod tabsize); { spaces to next tab
stop }
              IF (INSERTON) THEN BEGIN
                stuff_txt(SPACES(COL1));
                SHOW_LINE;
              END ELSE BEGIN
                INC(VCOLUMN,COL1);
                IF VCOLUMN>WIDTH THEN BEGIN
                  INC(OFFSET,SCRBUMP);
                  DEC(VCOLUMN,SCRBUMP);
                  SHOW_TXT;
                END;
                IF (POSITION+COL1)>E1 THEN BEGIN
                  POSITION:=E1; XLINE:=TRUE;
                END ELSE begin POSITION:=POSITION+COL1; XLINE:=FALSE; END;
{$IFDEF USEQWIK}
                GOTORC(R1+VROW-1,C1+VCOLUMN-1);
{$ELSE}
                GOTOXY(VCOLUMN,VROW);
{$ENDIF}
              END;
            END;
         25:BEGIN                      { CTRL-Y / ERASE LINE }
              GET_LINE_DATA(POSITION, b1,e1,col1);
              IF E1>TEXTSIZE THEN E1:=E1-B1 ELSE E1:=E1-B1+1;
              POSITION:=B1; { E1:=E1-B1+1; }
              OFFSET:=0; VCOLUMN:=1;
              DEL_CHARS(E1);
              SHOW_TXT;
              IF POSITION>TEXTSIZE THEN POSITION:=TEXTSIZE+1;
            END;
        ELSE BEGIN   {------------------ unspecific alphanumeric char }
          STUFF_TXT(CHR(C));           { store it }
          IF MAXCOLUMN=0 THEN BEGIN    { check for column boundaries }
            IF VCOLUMN>WIDTH THEN BEGIN
              INC(OFFSET,SCRBUMP);
              DEC(VCOLUMN,SCRBUMP);
              SHOW_TXT;
            END;
          END ELSE BEGIN  { limited screen/line size }
            SHOW_LINE;
            IF ((VCOLUMN+OFFSET)>MAXCOLUMN+1) THEN BEGIN  { hit edge limit }
              get_line_data(position,b1,e1,col1);
              word_wrap(b1,textsize,MAXCOLUMN);
            END ELSE BEGIN
              IF VCOLUMN>WIDTH THEN BEGIN { maxcolumn>width but set }
                INC(OFFSET,SCRBUMP);
                DEC(VCOLUMN,SCRBUMP);
                SHOW_TXT;
              END;
            END;
          END;
          SHOW_LINE;
        END;
      END; { case }
    END { alpha key }
  UNTIL LEAVING;
END;

(***************************************************************************)
PROCEDURE SETUP_TEXT_SETTINGS(Row1,Column1,Row2,Column2:BYTE;DRAWBOX:BOOLEAN);
{ sets appropriate system values for text window }
BEGIN
  R1:=ROW1; C1:=COLUMN1; R2:=ROW2; C2:=COLUMN2;  { set global position of win }
{ % these are arbitrary attribute values - tweak them to suit your tastes }
  NORMATTR:=37; { % }
  BACKATTR:=37; { % }
  BORDATTR:=36; { % }
  OFFSET:=0;
  INSERTON:=TRUE;
  XLINE:=FALSE;
  HEIGHT:=R2-R1+1;  { current height of text window }
  WIDTH:=C2-C1+1;   { current width (in columns) of text window }
  SCRBUMP:=WIDTH DIV 2;
  VROW:=1;          { virtual row and column of cursor inside text window }
  VCOLUMN:=1;

{ % maxcolumn sets automatic formatting and word wrapping !! }
{  MAXCOLUMN:=WIDTH; { set to 0 to disable word wrap and line length limits }
  MAXCOLUMN:=0;   { no word wrapping }

  position:=1;
  wintop:=1;
  IF DRAWBOX THEN BEGIN
    DRAW_BOX(R1,C1,R2,C2);
    { DRAW_BOX must be prior to initialize window if not using qwik }
    INITIALIZE_WINDOW(R1,C1,R2,C2);
  END;
END;
(***************************************************************************)
PROCEDURE EDIT(PT:POINTER;VAR RETURNCODE:BYTE);
{ Edit text; assumes text has already been initialized }
BEGIN
  TXT:=PT;    { assign specified text pointer to working name }
  SHOW_TXT;

  RETURNCODE:=PARSE_INPUT;
{ RETURNCODE the following values based on keys pressed:
      1=ESC
      2=ALT-X
      3=F1
      4=F10
      5=F2
}
END;
(***************************************************************************)
END. { Unit MPEDITOR.PAS }

[Back to TEXTEDIT SWAG index]  [Back to Main SWAG index]  [Original]