(*************************************************************************

:Program.    EdMovement.mod
:Contents.   Cursor-Movement for AmokEd
:Author.     Hartmut Goebel
:Language.   Oberon
:Translator. AmigaOberon V2.00
:History.    V0.1, 03 Dec 1990 Hartmut Goebel
:History.    V1.0, 14 Apr 1991 Hartmut Goebel [hG]
:History.    V1.0b 24 Apr 1991 [hG] -Bugs: doReturn, doRepeat, doIf
:History.    V1.0c 26 Apr 1991 [hG] ^doJoin, _etwas_ optimiert
:History.    V1.1  12 Sep 1991 [hG] + doReformat
:Date.       17 Oct 1991 21:16:35

*************************************************************************)

MODULE EdMovement;
(* $Debug- *)
IMPORT
  EdGadgets,
  e  : Exec,
  edB: EdBlocks,
  edD: EdDisplay,
  edE: EdErrors,
  edG: EdGlobalVars,
  edL: EdLowLevel,
  g  : Graphics,
  lst: EdLists,
  str: Strings,
  sys: SYSTEM;

CONST
  (* Flags für einzelne Kommandos *)
  pageSet* = 0; (* PageSet od. PageUp/Down *)
  pageUp* = 1;  (* PageUp od. PageDown *)
  rfmtParag* = 0;

  LineTooLong = "Line too long!";
  LineMarked = "Line marked";
  PingOutOfRange = "ping: out of range";
  PongRangeErrorOrNothingMarked = "pong: range error or nothing marked";

VAR
  PagePctg: INTEGER;

(*--------------------------------------------------------------------------*)

PROCEDURE doToMouse*;
BEGIN
  edD.TextPosition((edG.Mx-edG.XBase) DIV edG.XSize,
                   (edG.My-edG.YBase) DIV edG.YSize);
END doToMouse;

(*--------------------------------------------------------------------------*)

PROCEDURE^ doReformat*;  (* Forward, wird in doDel und doBS gebraucht *)

(*--------------------------------------------------------------------------*)

PROCEDURE doUp*;
BEGIN
  IF edG.Text.line # 0 THEN
    edD.PutBackLine;
    DEC(edG.Text.line);
    edG.Text.actLinePtr := edG.Text.actLinePtr.prev(edG.Line);
    edD.TextLoad;
    IF (edG.Text.line < edG.Text.topLine) THEN
      edG.Text.topLinePtr := edG.Text.actLinePtr;
      DEC(edG.Text.topLine);
      IF edG.NoScreenUpdate = 0 THEN
        edD.FastScrollRaster(0,-edG.YSize,edD.Col(0),edD.Row(0),
                       edD.Col(edG.Columns)-1,edD.Row(edG.Rows)-1);
        edD.TextDisplaySeg(0,1);
        EdGadgets.SetPropKnob;
      END;
    END;
  ELSE
    edG.Rc := edE.cmdFailed;
  END;
END doUp;


PROCEDURE doDown*;
BEGIN
  IF edG.Text.actLinePtr # edG.Text.lineList.tail THEN
    edD.PutBackLine;
    INC(edG.Text.line);
    edG.Text.actLinePtr := edG.Text.actLinePtr.next(edG.Line);
    edD.TextLoad;
    IF (edG.Text.line-edG.Text.topLine >= edG.Rows) THEN
      edG.Text.topLinePtr := edG.Text.topLinePtr.next(edG.Line);
      INC(edG.Text.topLine);
      IF edG.NoScreenUpdate = 0 THEN
        edD.FastScrollRaster(0,edG.YSize,edD.Col(0),edD.Row(0),
                       edD.Col(edG.Columns)-1,edD.Row(edG.Rows)-1);
        edD.TextDisplaySeg(edG.Rows-1,1);
        EdGadgets.SetPropKnob;
      END;
    END;
  ELSE
    edG.Rc := edE.cmdFailed;
  END;
END doDown;


PROCEDURE doScrollUp*;
BEGIN
  IF edG.Text.topLine > 0 THEN
    IF edG.NoScreenUpdate = 0 THEN
      edD.PutBackLine;
      edD.FastScrollRaster(0,-edG.YSize,edD.Col(0),edD.Row(0),
                     edD.Col(edG.Columns)-1,edD.Row(edG.Rows)-1);
      edG.Text.topLinePtr := edG.Text.topLinePtr.prev(edG.Line);
      edG.Text.actLinePtr := edG.Text.actLinePtr.prev(edG.Line);
      DEC(edG.Text.topLine);
      DEC(edG.Text.line);
      edD.TextLoad;
      edD.TextDisplaySeg(0,1);
      EdGadgets.SetPropKnob;
    END;
  ELSE
    edG.Rc := edE.cmdFailed;
  END;
END doScrollUp;


PROCEDURE doScrollDown*;
BEGIN
  IF edG.Text.topLine + edG.Rows < edG.Text.numberOfLines THEN
    IF edG.NoScreenUpdate = 0 THEN
      edD.PutBackLine;
      edD.FastScrollRaster(0,edG.YSize,edD.Col(0),edD.Row(0),
                     edD.Col(edG.Columns)-1,edD.Row(edG.Rows)-1);
      edG.Text.topLinePtr := edG.Text.topLinePtr.next(edG.Line);
      edG.Text.actLinePtr := edG.Text.actLinePtr.next(edG.Line);
      INC(edG.Text.topLine);
      INC(edG.Text.line);
      edD.TextLoad;
      edD.TextDisplaySeg(edG.Rows-1,1);
      EdGadgets.SetPropKnob;
    END;
  ELSE
    edG.Rc := edE.cmdFailed;
  END;
END doScrollDown;


PROCEDURE doDownAdd*;
VAR
  newLine : edG.LinePtr;
BEGIN
  IF edG.Text.line+1 = edG.Text.numberOfLines THEN
    edL.NewLine(newLine,edG.ChunkSize);
    IF newLine # NIL THEN
      lst.AddTail(edG.Text.lineList,newLine);
      INC(edG.Text.numberOfLines);
      INCL(edG.Text.status,edG.modified);
    END;
  END;
  doDown;
END doDownAdd;

(*--------------------------------------------------------------------------*)

PROCEDURE doTop*;
BEGIN
  edD.PutBackLine;
  edG.Text.line := 0;
  edG.Text.actLinePtr :=edG.Text.lineList.head(edG.Line);
  edD.TextLoad;
  edD.TextSync;
  EdGadgets.SetPropKnob;
END doTop;


PROCEDURE doBottom*;
BEGIN
  edD.PutBackLine;
  edG.Text.line := edG.Text.numberOfLines-1;
  edG.Text.actLinePtr :=edG.Text.lineList.tail(edG.Line);
  (*edG.Text.topLine := edG.Text.line-edG.Rows;
  IF edG.Text.topLine < 0 THEN
    edG.Text.topLine := 0; END;*)
  edD.TextLoad;
  edD.TextSync;
  EdGadgets.SetPropKnob;
END doBottom;


PROCEDURE doScreenTop*;
BEGIN
  edD.PutBackLine;
  edG.Text.line := edG.Text.topLine;
  edG.Text.actLinePtr := edG.Text.topLinePtr;
  edD.TextLoad;
  edD.TextSync;
END doScreenTop;


PROCEDURE doScreenBottom*;
BEGIN
  edD.PutBackLine;
  edG.Text.line := edG.Text.topLine + edG.Rows - 1;
  IF edG.Text.line >= edG.Text.numberOfLines THEN
    edG.Text.line := edG.Text.numberOfLines - 1; END;
  edG.Text.actLinePtr := edG.Text.topLinePtr;
  lst.GoForwardNil(edG.Text.actLinePtr,edG.Text.line-edG.Text.topLine);
  edD.TextLoad;
  edD.TextSync;
END doScreenBottom;

(*--------------------------------------------------------------------------*)

PROCEDURE doLeft*;
BEGIN
  IF edG.Text.pos #0 THEN
    DEC(edG.Text.pos);
    IF edG.Text.pos < edG.Text.topPos THEN
      edD.TextSync; END;
  ELSE
    edG.Rc := edE.cmdFailed;
  END;
END doLeft;


PROCEDURE doRight*;
BEGIN
  IF edG.Text.pos < edG.MaxLineLength-2 THEN
    IF edG.Text.pos = edG.LineBufferLen THEN
      edG.LineBuffer[edG.LineBufferLen] := 20X;
      INC(edG.LineBufferLen);
      edG.LineBuffer[edG.LineBufferLen] := 0X;
    END;
    INC(edG.Text.pos);
    IF (edG.Text.pos-edG.Text.topPos >= edG.Columns) THEN
      edD.TextSync; END;
  ELSE
    edG.Rc := edE.cmdFailed;
  END;
END doRight;


PROCEDURE doWordLeft*;
VAR
  i: INTEGER;
BEGIN
  LOOP
    i := edG.Text.pos;
    IF i # 0 THEN
      REPEAT
        DEC(i);
      UNTIL (i=0) OR (edL.IsAscii(edG.LineBuffer[i]));
    END;
    IF (edG.LineBufferLen=0) OR (edG.Text.pos=0) THEN
      IF (edG.commLineMode IN edG.Status) OR (edG.Text.line = 0) THEN
        EXIT;
      END;
      edD.PutBackLine;
      edG.Text.actLinePtr := edG.Text.actLinePtr.prev(edG.Line);
      DEC(edG.Text.line);
      edG.Text.pos := 0; (* damit's schneller geht *)
      edD.TextLoad;
      edG.Text.pos := edG.LineBufferLen;
    ELSE
      WHILE (i#0) AND (edL.IsAscii(edG.LineBuffer[i])) DO
        DEC(i); END;
      IF (i#0) AND NOT (edL.IsAscii(edG.LineBuffer[i])) THEN
        INC(i); END;
      EXIT;
    END;
  END;
  edG.Text.pos := i;
  edD.TextSync;
END doWordLeft;


PROCEDURE doWordRight*;
VAR
  i: INTEGER;
BEGIN
  LOOP
    i := edG.Text.pos;
    IF i # edG.LineBufferLen THEN
      WHILE (i<edG.LineBufferLen) AND (edL.IsAscii(edG.LineBuffer[i])) DO
        INC(i); (* zum Wordende *)
      END;
      WHILE (i<edG.LineBufferLen) AND NOT (edL.IsAscii(edG.LineBuffer[i])) DO
        INC(i); (* zum nächsten Wort *)
      END;
      EXIT;
    ELSE
      IF (edG.commLineMode IN edG.Status)
      OR (edG.Text.line = edG.Text.numberOfLines -1) THEN
        i := edG.Text.pos;
        EXIT;
      END;
      edD.PutBackLine;
      edG.Text.actLinePtr := edG.Text.actLinePtr.next(edG.Line);
      INC(edG.Text.line);
      edG.Text.pos := 0; (* wg. Auffüllen mit Spaces *)
      edD.TextLoad;
      IF edL.IsAscii(edG.LineBuffer[0]) THEN
        i := 0;
        EXIT;
      END;
      edG.Text.pos := 0;
    END;
  END;
  edG.Text.pos := i;
  edD.TextSync;
END doWordRight;

(*--------------------------------------------------------------------------*)

PROCEDURE doFirstColumn*;
BEGIN
  edL.StripEndSpaces;
  IF edG.Text.pos # 0 THEN edG.Text.pos := 0; edD.TextSync; END;
END doFirstColumn;


PROCEDURE doFirstNb*;
VAR
  i: INTEGER;
BEGIN
  edL.StripEndSpaces;
  i := 0;
  WHILE edG.LineBuffer[i] = 20X DO INC(i); END;
  IF edG.LineBuffer[i] = 0X THEN i := 0; END;
  edG.Text.pos := i;
  edD.TextSync;
END doFirstNb;


PROCEDURE doLastColumn*;
VAR
  i: INTEGER;
BEGIN
  edL.StripEndSpaces;
  edG.Text.pos := edG.LineBufferLen;
  edD.TextSync;
END doLastColumn;

(*--------------------------------------------------------------------------*)

PROCEDURE doTab*;
VAR
  n: INTEGER;
BEGIN
  n := SHORT(edG.Text.tabStop-(edG.Text.pos MOD edG.Text.tabStop));
  REPEAT
    doRight;
    DEC(n);
  UNTIL n = 0;
END doTab;


PROCEDURE doBackTab*;
VAR
  n: INTEGER;
BEGIN
  n := SHORT(edG.Text.pos MOD edG.Text.tabStop);
  IF n = 0 THEN  (* genau auf Tabposition *)
    n := SHORT(edG.Text.tabStop); END;
  REPEAT
    doLeft;
    DEC(n);
  UNTIL n = 0;
END doBackTab;

(*--------------------------------------------------------------------------*)

PROCEDURE doDel*;
VAR
  buf : edG.StringPtr;
BEGIN
  IF edG.LineBuffer[edG.Text.pos] # 0X THEN
    str.Delete(edG.LineBuffer,edG.Text.pos,1);
    DEC(edG.LineBufferLen);
    edD.FastScrollRaster(edG.XSize,0,
           edD.Col(edG.Text.pos-edG.Text.topPos),
           edD.Row(SHORT(edG.Text.line-edG.Text.topLine)),
           edD.Col(edG.Columns)-1,
           edD.Row(SHORT(edG.Text.line-edG.Text.topLine+1))-1);
    IF (edG.LineBufferLen >= edG.Text.topPos+edG.Columns) THEN
      edD.SetPen(edG.Text.line);
      g.Move(edG.RPort,edD.ColT(edG.Columns-1),
            edD.RowT(SHORT(edG.Text.line-edG.Text.topLine)));
      buf := sys.ADR(edG.LineBuffer);
      INC(buf,edG.Text.topPos+edG.Columns-1);
      g.Text(edG.RPort,buf^,1);
    END;
    IF NOT (edG.commLineMode IN edG.Status)
    AND (edG.wordWrap IN edG.Text.status) THEN
      edG.ArgSet := {};
      doReformat;
    END;
  ELSE
    edG.Rc := edE.cmdFailed;
  END;
END doDel;


PROCEDURE doBs*;
VAR
  buf : edG.StringPtr;
BEGIN
  IF edG.Text.pos # 0 THEN
    str.Delete(edG.LineBuffer,edG.Text.pos-1,1);
    DEC(edG.Text.pos);
    DEC(edG.LineBufferLen);
    IF edG.Text.pos < edG.Text.topPos THEN
      edD.TextSync;
    ELSE
      edD.FastScrollRaster(edG.XSize, 0,
           edD.Col(edG.Text.pos-edG.Text.topPos),
           edD.Row(SHORT(edG.Text.line-edG.Text.topLine)),edD.Col(edG.Columns)-1,
           edD.Row(SHORT(edG.Text.line-edG.Text.topLine+1))-1);
      IF (edG.LineBufferLen >= edG.Text.topPos+edG.Columns) THEN
        edD.SetPen(edG.Text.line);
        g.Move(edG.RPort,edD.ColT(edG.Columns-1),
               edD.RowT(SHORT(edG.Text.line-edG.Text.topLine)));
        buf := sys.ADR(edG.LineBuffer);
        INC(buf,edG.Text.topPos+edG.Columns);
        g.Text(edG.RPort,buf^,1);
      END;
    END;
    IF NOT (edG.commLineMode IN edG.Status)
    AND (edG.wordWrap IN edG.Text.status) THEN
      edG.ArgSet := {};
      doReformat;
    END;
  ELSE
    edG.Rc := edE.cmdFailed;
  END;
END doBs;


PROCEDURE doRemEOL*;
BEGIN
  edG.LineBufferLen := edG.Text.pos;
  edG.LineBuffer[edG.LineBufferLen] := 0X;
  edD.TextRedisplayCurrentLine;
END doRemEOL;

(*----------------------------------------------------------------------*)

PROCEDURE InsertLine(newLine: edG.LinePtr; Behind: BOOLEAN);
BEGIN
  edD.PutBackLine;
  IF Behind THEN
    lst.AddBehind(edG.Text.lineList,newLine,edG.Text.actLinePtr);
    edB.HoldBlockLines(FALSE);
  ELSE
    lst.AddBefore(edG.Text.lineList,newLine,edG.Text.actLinePtr);
    IF edG.Text.topLinePtr = edG.Text.actLinePtr THEN
      edG.Text.topLinePtr := newLine; END;
    edG.Text.actLinePtr := newLine;
    edD.TextLoad;
    DEC(edG.Text.line);
    edB.HoldBlockLines(FALSE);
    INC(edG.Text.line);
  END;
  INC(edG.Text.numberOfLines);
  INCL(edG.Text.status,edG.modified);
  IF edG.NoScreenUpdate = 0 THEN
    g.ScrollRaster(edG.RPort,0,-edG.YSize,edD.Col(0),
                   edD.Row(SHORT(edG.Text.line-edG.Text.topLine)),
                   edD.Col(edG.Columns)-1,edD.Row(edG.Rows)-1);
    edD.TextDisplaySeg(SHORT(edG.Text.line-edG.Text.topLine),2);
  END;
END InsertLine;


PROCEDURE doInsLine*;
VAR
  newLine: edG.LinePtr;
BEGIN
  edL.NewLine(newLine,edG.ChunkSize);
  IF edG.memoryFail IN edG.Status THEN
    edG.Rc := edE.cmdSevere;
    RETURN;
  END;
  InsertLine(newLine,FALSE);
END doInsLine;


PROCEDURE doUndelLine*;
VAR
  newLine: edG.LinePtr;
BEGIN
  IF edG.DelLinePtr = NIL THEN edG.Rc := edE.cmdFailed; RETURN END;
  newLine := edL.CreateLineCopy(edG.DelLinePtr);
  IF newLine=NIL THEN edG.Rc := edE.cmdSevere; RETURN END;
  InsertLine(newLine,FALSE);
END doUndelLine;


PROCEDURE doDelLine*;
VAR
  This: edG.LinePtr;
BEGIN
  IF edG.Text.numberOfLines > 1 THEN
    INCL(edG.Text.status,edG.modified);
    edD.PutBackLine;
    This := edG.Text.actLinePtr;
    edB.HoldBlockLines(TRUE);
    edG.Text.actLinePtr := edG.Text.actLinePtr.next(edG.Line);
    lst.Remove(edG.Text.lineList,This);
    IF edG.LineBufferLen > 0 THEN (* nur retten, wenn was drin steht *)
      IF edG.DelLinePtr # NIL THEN
        e.FreeMem(edG.DelLinePtr(edG.Line).string,edG.DelLinePtr(edG.Line).len);
        e.FreeMem(edG.DelLinePtr,edG.LineAllocSize);
      END;
      edG.DelLinePtr := This;
    ELSE
      e.FreeMem(This(edG.Line).string,This(edG.Line).len);
      e.FreeMem(This,edG.LineAllocSize);
    END;
    DEC(edG.Text.numberOfLines);
    IF edG.Text.actLinePtr = NIL THEN (* es war die letzte Ziele *)
      edG.Text.actLinePtr := edG.Text.lineList.tail(edG.Line);
      DEC(edG.Text.line);    (* eine Zeile vor *)
    END;
    edD.TextLoad;
    IF edG.Text.line < edG.Text.topLine THEN
      edD.TextSync;
      RETURN;
    ELSIF edG.Text.line = edG.Text.topLine THEN
      edG.Text.topLinePtr := edG.Text.actLinePtr;
    END;
    IF edG.NoScreenUpdate = 0 THEN
      g.ScrollRaster(edG.RPort,0,edG.YSize,edD.Col(0),
                     edD.Row(SHORT(edG.Text.line-edG.Text.topLine+1)),
                     edD.Col(edG.Columns)-1,edD.Row(edG.Rows)-1);
      edD.TextRedisplayCurrentLine;
      edD.TextDisplaySeg(edG.Rows-1,1);
    END;
  ELSE
    edG.Text.pos := 0;
    doRemEOL;
    EXCL(edG.Text.status,edG.modified);
  END;
END doDelLine;


PROCEDURE doJoin*;
VAR
  i: INTEGER;
  next: edG.LinePtr;
BEGIN
  edL.StripEndSpaces;
  i := edG.LineBufferLen;
  IF (edG.Text.line+1 < edG.Text.numberOfLines)
  AND (i+str.Length(next(edG.Line).string^)<edG.MaxLineLength)
  THEN
    next := edG.Text.actLinePtr.next(edG.Line);
    IF (i # 0) AND (edG.LineBuffer[i] # 20X) THEN
      edG.LineBuffer[i] := " "; edG.LineBuffer[i+1] := "\o";
    END;
    i := 0;
    WHILE next(edG.Line).string[i] = 20X DO
      INC(i); END;
    IF i > 0 THEN str.Delete(next(edG.Line).string^,0,i); END;
    str.Append(edG.LineBuffer,next(edG.Line).string^);
    edG.LineBufferLen := str.Length(edG.LineBuffer);
    edD.PutBackLine;
    INC(edG.Text.line);
    edB.HoldBlockLines(TRUE);
    DEC(edG.Text.line);
    edL.FreeLines(edG.Text.lineList,next,next);
    DEC(edG.Text.numberOfLines);
    edD.TextLoad;
    IF edG.NoScreenUpdate = 0 THEN
      g.ScrollRaster(edG.RPort,0,edG.YSize,edD.Col(0),
                     edD.Row(SHORT(edG.Text.line-edG.Text.topLine+1)),
                     edD.Col(edG.Columns)-1,edD.Row(edG.Rows)-1);
      edD.TextRedisplayCurrentLine;
      edD.TextDisplaySeg(edG.Rows-1,1);
    END;
  ELSE
    edG.Rc := edE.cmdFailed;
  END;
END doJoin;


PROCEDURE doSplit*;
VAR
  len : INTEGER;
  newLine : edG.LinePtr;
BEGIN
  len := edG.LineBufferLen-edG.Text.pos;
  edL.NewLine(newLine,len+edG.ChunkSize-(len MOD edG.ChunkSize));
  IF edG.memoryFail IN edG.Status THEN
    edG.Rc := edE.cmdSevere;
    RETURN;
  END;
  str.Cut(edG.LineBuffer,edG.Text.pos,len,newLine(edG.Line).string^);
  edG.LineBuffer[edG.Text.pos] := 0X;
  len := SHORT(edG.Text.line-edG.Text.topLine);
  g.SetAPen(edG.RPort,0);
  g.RectFill(edG.RPort,edD.Col(0),edD.Row(len),
             edG.XBase+edG.XPixs,edD.Row(len+1)-1);
  InsertLine(newLine,TRUE);
  doDown;
  doUp;
END doSplit;

(*----------------------------------------------------------------------*)

(*
 * GOTO [+/-]N
 * GOTO BLOCK   start of block
 * GOTO START   start of block
 * GOTO END     end of block
 *)

PROCEDURE JumpToLine*(n: LONGINT);
BEGIN
  IF n >= edG.Text.numberOfLines THEN
    n := edG.Text.numberOfLines - 1; END;
  IF (n >= 0) AND (n # edG.Text.line) THEN
    edD.PutBackLine;
    IF n = 0 THEN
      edG.Text.actLinePtr :=edG.Text.lineList.head(edG.Line);
    ELSIF n = edG.Text.numberOfLines-1 THEN
      edG.Text.actLinePtr :=edG.Text.lineList.tail(edG.Line);
    ELSIF n < edG.Text.line THEN
      lst.GoBackwardNil(edG.Text.actLinePtr,edG.Text.line-n);
    ELSE
      lst.GoForwardNil(edG.Text.actLinePtr,n-edG.Text.line);
    END;
    edG.Text.line := n;
    edD.TextLoad;
    edD.TextSync;
    EdGadgets.SetPropKnob;
  END;
END JumpToLine;


PROCEDURE doGoto*;
VAR
  n : LONGINT;
  ok : BOOLEAN;
BEGIN
  CASE CAP(edG.Arg[0][0]) OF
  |"B","S": IF edG.Text = edG.Block.Owner THEN
              edG.Text.line := edG.Block.SNum;
              edG.Text.actLinePtr := edG.Block.mark.head;
            ELSE
              edG.Text.line := 0;
              edG.Text.actLinePtr := edG.Text.lineList.head;
            END;
  |"E": IF edG.Text = edG.Block.Owner THEN
              edG.Text.line := edG.Block.ENum;
              edG.Text.actLinePtr := edG.Block.mark.tail;
            ELSE
              edG.Text.line := edG.Text.numberOfLines-1;
              edG.Text.actLinePtr := edG.Text.lineList.tail;
            END;
  |ELSE IF NOT edL.StrToInt(edG.Arg[0],n) THEN
          edG.Rc := edE.cmdError;
          edL.Title(edG.BadArgument);
          RETURN;
        END;
        IF edG.Arg[0][0] < "0" THEN (* mit Vorzeichen => relativ *)
          n := n+edG.Text.line;
        ELSE
          DEC(n); (* intern bei Null anfangen *)
        END;
        JumpToLine(n);
        RETURN;
  END;
  edD.TextSync;
END doGoto;

(*----------------------------------------------------------------------*)

(*
 *  PAGEUP
 *  PAGEDOWN
 *  PAGESET n   (n = 0 to 100 for percentage of #rows to scroll, minimum 1)
 *              can be > 100.
 *)

PROCEDURE doPage*;
VAR
  n: INTEGER;
  long: LONGINT;
BEGIN
  IF pageSet IN edG.ArgSet THEN
    IF edL.StrToInt(edG.Arg[0],long) THEN
      PagePctg := ABS(SHORT(long));
    ELSE
      edL.Title(edG.BadArgument); edG.Rc := edE.cmdFailed;
    END;
    RETURN;
  END;
  n := edG.Rows * PagePctg DIV 100; IF n = 0 THEN n := 1; END;
  IF pageUp IN edG.ArgSet THEN n := -n; END;
  IF (n > 0) AND (edG.Text.topLine >= edG.Text.numberOfLines - edG.Rows) THEN
      RETURN; END;
  edD.PutBackLine;
  INC(edG.Text.line,n);     (* alle Werte per Hand setzen, da TextSync    *)
  INC(edG.Text.topLine,n);  (* sonst nur einen halben Bildschirm scrollt! *)
  IF edG.Text.line >= edG.Text.numberOfLines THEN
    edG.Text.line := edG.Text.numberOfLines - 1;
  ELSIF edG.Text.line < 0 THEN
    edG.Text.line := 0;
  END;
  IF edG.Text.topLine >= edG.Text.numberOfLines THEN
    edG.Text.topLine := edG.Text.numberOfLines - edG.Rows - 1;
  ELSIF edG.Text.topLine < 0 THEN
    edG.Text.topLine := 0;
  END;
  IF n < 0 THEN
    lst.GoBackwardNil(edG.Text.actLinePtr,-n);
    IF edG.Text.actLinePtr = NIL THEN
      edG.Text.actLinePtr := edG.Text.lineList.head(edG.Line); END;
    edG.Text.topLinePtr := edG.Text.actLinePtr;
    lst.GoBackwardNil(edG.Text.topLinePtr,edG.Text.line-edG.Text.topLine);
  ELSE
    lst.GoForwardNil(edG.Text.actLinePtr,n);
    IF edG.Text.actLinePtr = NIL THEN
      edG.Text.actLinePtr := edG.Text.lineList.tail(edG.Line); END;
    edG.Text.topLinePtr := edG.Text.actLinePtr;
    lst.GoBackwardNil(edG.Text.topLinePtr,edG.Text.line-edG.Text.topLine);
  END;
  edD.TextLoad;
  edD.TextSync;
  IF NOT(edG.alreadyRedrawn IN edG.Status) THEN edD.TextRedisplay; END;
END doPage;

(*----------------------------------------------------------------------*)

PROCEDURE doPing*;
VAR
  long: LONGINT;
BEGIN
  IF edL.StrToInt(edG.Arg[0],long) AND (long<edG.NumPingPongs) THEN
    edG.PingPong[long].txt  := edG.Text;
    edG.PingPong[long].line := edG.Text.line;
    edG.PingPong[long].pos  := edG.Text.pos;
    edG.Rc := edE.cmdValid2; edL.Title(LineMarked);
  ELSE
    edG.Rc := edE.cmdFailed; edL.Title(PingOutOfRange);
  END;
END doPing;


PROCEDURE doPong*;
VAR
  long: LONGINT;
BEGIN
  IF edL.StrToInt(edG.Arg[0],long) AND (long<edG.NumPingPongs)
  AND (edG.PingPong[long].txt # NIL)
  AND (edG.PingPong[long].line<edG.Text.numberOfLines) THEN
    INC(edG.NoScreenUpdate);
    edD.SwitchEdit(edG.PingPong[long].txt);
    edG.Text.pos := edG.PingPong[long].pos;
    DEC(edG.NoScreenUpdate);
    JumpToLine(edG.PingPong[long].line);
  ELSE
    edG.Rc := edE.cmdFailed; edL.Title(PongRangeErrorOrNothingMarked);
  END;
END doPong;


PROCEDURE ClearPingPongs;
VAR
  i: INTEGER;
BEGIN
  i := edG.NumPingPongs;
  REPEAT
    DEC(i);
    edG.PingPong[i].txt := NIL;
  UNTIL i = 0;
END ClearPingPongs;

(*-------------------------------------------------------------------------*)

(* schreibt <text> in den Buffer und auf den Scrren, beachtet InsertMode *)

PROCEDURE TextWrite*(text: edG.StringPtr);
VAR
  len,i : INTEGER;
  buf : edG.StringPtr;
BEGIN
  len := str.Length(text^);
  IF edG.LineBufferLen+len >= edG.MaxLineLength-1 THEN
    edD.TextLoad;
    edD.TextSync;
  END;
  IF NOT (edG.insertMode IN edG.Text.status) THEN
    IF (edG.Text.pos+len >= edG.MaxLineLength-1) THEN
      edL.Title(LineTooLong); edG.Rc := edE.cmdFailed;
      RETURN;
    END;
    buf := sys.ADR(edG.LineBuffer[edG.Text.pos]);
    e.CopyMem(text^,buf^,len);
    IF (edG.Text.pos+len > edG.LineBufferLen) THEN
      edG.LineBufferLen := edG.Text.pos+len;
      edG.LineBuffer[edG.LineBufferLen] := 0X;
    END;
  ELSE
    IF (edG.LineBufferLen+len >= edG.MaxLineLength-1) THEN
      edL.Title(LineTooLong); edG.Rc := edE.cmdFailed;
      RETURN;
    END;
    str.Insert(edG.LineBuffer,edG.Text.pos,text^);
    INC(edG.LineBufferLen,len);
    IF (len < edG.Columns - (edG.Text.pos-edG.Text.topPos)) THEN
      edD.FastScrollRaster(-len*edG.XSize,0,
         edD.Col(edG.Text.pos-edG.Text.topPos),
         edD.Row(SHORT(edG.Text.line-edG.Text.topLine)),edD.Col(edG.Columns),
         edD.Row(SHORT(edG.Text.line-edG.Text.topLine+1))-1);
    END;
  END; (* IF NOT InsertMode *)
  IF (edG.Text.pos-edG.Text.topPos+len > edG.Columns) THEN
    i := edG.Columns-edG.Text.pos+edG.Text.topPos;
  ELSE
    i := len;
  END;
  edD.SetPen(edG.Text.line);
  g.Move(edG.RPort,edD.ColT(edG.Text.pos-edG.Text.topPos),
         edD.RowT(SHORT(edG.Text.line-edG.Text.topLine)));
  g.Text(edG.RPort,text^,i);
  INC(edG.Text.pos,len);
  IF (edG.Text.pos-edG.Text.topPos >= edG.Columns) THEN
    edD.TextSync; END;
  IF NOT (edG.commLineMode IN edG.Status) AND (edG.NoScreenUpdate = 0)
  AND (edG.wordWrap IN edG.Text.status) THEN
    doReformat; END;
END TextWrite;

(*-------------------------------------------------------------------------*)
(*
 *  rfmtParag  : force reformat entire paragraph
 * ~rfmtParag  : only until line equalizes (from TextWrite())
 *
 * What is a paragraph?   A paragraph ends whenever the left justification
 * gets larger, or on a blank line.
 *)
(* $Debug= *)
PROCEDURE doReformat*;
VAR
  string: edG.StringPtr;
  lnsc, fnst, fnsc: INTEGER;
  column: INTEGER;
  srow, crow, erow: LONGINT;
  cptr: edG.LinePtr;     (* origin line *)
  dins: INTEGER;         (* relative insert lines/delete lines   *)
  moded: BOOLEAN;        (* any modifications done at all?       *)
  checked: BOOLEAN;      (* for cursor positioning.              *)
  nlok: BOOLEAN;
  i, indent: INTEGER;
BEGIN
  column := edG.Text.pos;
  srow := edG.Text.line; erow := srow; crow := srow;
  cptr  := edG.Text.actLinePtr;
  dins := 0; moded := FALSE; checked := FALSE;
  IF (edG.Text.margin = 0) THEN edG.Text.margin := 75; END;
  INC(edG.NoScreenUpdate);

  LOOP
    string := edG.Text.actLinePtr.next(edG.Line).string;
    fnsc := edL.FirstNoSpace(sys.ADR(edG.LineBuffer));
    fnst := edL.FirstNoSpace(string);
    nlok := (edG.Text.line + 1 < edG.Text.numberOfLines) AND (fnsc >= fnst);
    IF string[0] = 0X THEN
      nlok := FALSE; END;
    lnsc := edL.LastNoSpace(sys.ADR(edG.LineBuffer));
    IF (lnsc < edG.Text.margin) THEN
      (* space at end of line for margin-lnsc-2 letter word *)
      IF NOT nlok THEN      (* but no more data to joinup *)
        EXIT; END;          (* done *)
      IF (edG.Text.margin-lnsc-2 >= edL.WordLen(sys.ADR(string[fnst]))) THEN
        edG.Text.pos := 0;
        edG.LineBufferLen := edL.LastNoSpace(sys.ADR(edG.LineBuffer));
        IF (edG.LineBuffer[edG.LineBufferLen] # 0X) THEN
          INC(edG.LineBufferLen); END;
        moded := TRUE;
        DEC(dins);
        doJoin;
        IF edG.Rc >= edE.cmdFailed THEN
          INC(dins);
          edL.Title(edG.ErrorMarginOver124);
          EXIT;          (* stop reformating *)
        END;
      ELSE
        IF NOT (rfmtParag IN edG.ArgSet) THEN   (* if couldn't mod line, and *)
          EXIT; END;                     (* TextWrite, don't update any more *)
        doDown;
        erow := edG.Text.line;
      END;
    ELSE                    (* no space, need to split      *)
      LOOP                  (* find start of prev word      *)
        WHILE (lnsc # 0) & (edG.LineBuffer[lnsc] # ' ') DO
          DEC(lnsc); END;
        IF (lnsc < edG.Text.margin) THEN
          EXIT; END;
        DEC(lnsc);          (* edG.Text.margin >= 1 !! *)
      END;
      IF (lnsc # 0) THEN             (* ok to split at word          *)
        INC(lnsc);
        INC(dins);
        edG.Text.pos := lnsc;
        doSplit;               (* Split at point LNSC          *)
        edG.Text.pos := 0;     (* avoid FillUpSpaces *)
        doDown;                (* must insert proper amount?   *)
        IF nlok THEN
          indent :=  fnst;
        ELSE
          indent := fnsc;
        END;
        IF NOT checked THEN
          checked := TRUE;
          IF (lnsc <= column) THEN   (* if split before cursor   *)
            column := column - edG.Text.pos + indent;
            cptr := cptr.next(edG.Line); INC(crow);
          END;
        END;
        IF (indent # 0) AND (edG.LineBufferLen + indent < 253) THEN
          i := str.Length(edG.LineBuffer)+1;
          REPEAT
            edG.LineBuffer[indent+i] := edG.LineBuffer[i];
            DEC(i);
          UNTIL i < 0;
          INC(edG.LineBufferLen,indent);
          REPEAT
            DEC(indent);
            edG.LineBuffer[indent] := " ";
          UNTIL indent = 0;
        END;
        erow := edG.Text.line;
      ELSE
        IF NOT (rfmtParag IN edG.ArgSet) THEN EXIT; END;
        doDown;
      END;
    END;
  END; (* LOOP *)

  IF (column < 0) OR (column > 200) THEN
    column := 0; END;
  edD.PutBackLine;
  edG.Text.actLinePtr := cptr;
  edG.Text.line := crow;
  edG.Text.pos := column;
  DEC(edG.NoScreenUpdate);
  edD.TextLoad;
  edD.TextSync;
  IF (dins # 0) OR (srow >= edG.Text.numberOfLines)
  OR (srow < edG.Text.topLine) OR (srow >= edG.Text.topLine + edG.Rows) THEN
    IF (srow >= edG.Text.numberOfLines) THEN
      srow := edG.Text.numberOfLines - 1;
    END;
    IF NOT (edG.alreadyRedrawn IN edG.Status) THEN
      edD.TextRedisplay; END;
  ELSE
    IF (erow = srow) AND moded THEN
      edD.TextRedisplayCurrentLine;
    ELSIF NOT (edG.alreadyRedrawn IN edG.Status) THEN
      INC(erow);
      IF (erow - edG.Text.topLine > edG.Rows) THEN
        erow := edG.Text.topLine + edG.Rows; END;
      g.SetAPen(edG.RPort, 0);
      g.RectFill(edG.RPort, edD.Col(0), edD.Row(SHORT(srow - edG.Text.topLine)),
                 edG.XBase+edG.XPixs, edD.Row(SHORT(erow - edG.Text.topLine))-1);
      g.SetAPen(edG.RPort, 1);
      edD.TextDisplaySeg(SHORT(srow-edG.Text.topLine),SHORT(erow-srow));
    END;
  END;
  edG.Text.pos := column;
  edL.FillUpSpaces;
END doReformat;
(* $Debug-*)
(*-----------------------------------------------------------------------*)

BEGIN
  PagePctg := 80;
  ClearPingPongs;
END EdMovement.
