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

:Program.    EdMovement.mod
:Contents.   Cursor-Movement for AmokEd
:Author.     Hartmut Goebel
:Language.   Oberon
:Translator. AmigaOberon V2.13
: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: dReturn, dRepeat, dIf
:History.    V1.0c 26 Apr 1991 [hG] ^dJoin, _etwas_ optimiert
:History.    V1.1  12 Sep 1991 [hG] + dReformat
:History.    V1.2  30 Nov 1991 [hG] aus Version vom 17.Okt.1992 neu erstellt
:Date.       01 Feb 1992 01:30:44

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

MODULE EdMovement;

IMPORT
  e  : Exec,
  edB: EdBlocks,
  edD: EdDisplay,
  edE: EdErrors,
  edG: EdGlobalVars,
  eGd: EdGadgets,
  edL: EdLowLevel,
  g  : Graphics,
  I  : Intuition,
  lst: EdLists,
  str: Strings,
  s  : 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 dToMouse*;
BEGIN
  edD.TextPosition((edG.Mx-edG.XBase) DIV edG.XSize,
                   (edG.My-edG.YBase) DIV edG.YSize);
END dToMouse;

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

PROCEDURE dUp*;
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);
        eGd.SetProp;
      END;
    END;
  ELSE
    edG.Rc := edE.cmdFailed5;
  END;
END dUp;


PROCEDURE dDown*;
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);
        eGd.SetProp;
      END;
    END;
  ELSE
    edG.Rc := edE.cmdFailed5;
  END;
END dDown;


PROCEDURE dScrollUp*;
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);
      eGd.SetProp;
    END;
  ELSE
    edG.Rc := edE.cmdFailed5;
  END;
END dScrollUp;


PROCEDURE dScrollDown*;
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);
      eGd.SetProp;
    END;
  ELSE
    edG.Rc := edE.cmdFailed5;
  END;
END dScrollDown;

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

PROCEDURE dTop*;
BEGIN
  edD.PutBackLine;
  edG.Text.line := 0;
  edG.Text.actLinePtr :=edG.Text.lineList.head(edG.Line);
  edD.TextLoad;
  edD.TextSync(FALSE);
  eGd.SetProp;
END dTop;


PROCEDURE dBottom*;
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(FALSE);
  eGd.SetProp;
END dBottom;


PROCEDURE dScreenTop*;
BEGIN
  edD.PutBackLine;
  edG.Text.line := edG.Text.topLine;
  edG.Text.actLinePtr := edG.Text.topLinePtr;
  edD.TextLoad;
  edD.TextSync(FALSE);
END dScreenTop;


PROCEDURE dScreenBottom*;
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(FALSE);
END dScreenBottom;

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

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


PROCEDURE dRight*;
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(FALSE); END;
  ELSE
    edG.Rc := edE.cmdFailed5;
  END;
END dRight;


PROCEDURE dWordLeft*;
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(FALSE);
END dWordLeft;


PROCEDURE dWordRight*;
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(FALSE);
END dWordRight;

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

PROCEDURE dFirstColumn*;
BEGIN
  edL.StripEndSpaces;
  IF edG.Text.pos # 0 THEN edG.Text.pos := 0; edD.TextSync(FALSE); END;
END dFirstColumn;


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


PROCEDURE dLastColumn*;
BEGIN
  edL.StripEndSpaces;
  edG.Text.pos := edG.LineBufferLen;
  edD.TextSync(FALSE);
END dLastColumn;

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

PROCEDURE dTab*;
VAR
  n: INTEGER;
  (* $ClearVars- *)
BEGIN
  (* $ClearVars= *)
  n := SHORT(edG.Text.tabStop-(edG.Text.pos MOD edG.Text.tabStop));
  IF edG.Text.pos+n < edG.MaxLineLength-1 THEN
    REPEAT
      dRight;
      DEC(n);
    UNTIL n = 0;
  ELSE
    edG.Rc := edE.cmdFailed5;
  END;
END dTab;


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

(*--------------------------------------------------------------------------*)
(*
 * 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(FALSE);
    eGd.SetProp;
  END;
END JumpToLine;


PROCEDURE dGotoLine*;
VAR
  n : LONGINT;
  ok : BOOLEAN;
BEGIN
  edD.PutBackLine;
  CASE CAP(edG.Arg[0][0]) OF
  |"B","S":
    IF (edG.Text = edG.Block.Owner) & ~(edG.Block.ENum < 0) 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) & ~(edG.Block.ENum < 0) 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;  (* CASE *)
  edD.TextLoad;
  edD.TextSync(FALSE);
END dGotoLine;


PROCEDURE dGotoCol*;
VAR
  long: LONGINT;
  (* $ClearVars- *)
BEGIN
  (* $ClearVars= *)
  IF edL.StrToInt(edG.Arg[0],long) AND (long < edG.MaxLineLength) THEN
    IF edG.Arg[0][0] < "0" THEN (* mit Vorzeichen => relativ *)
      INC(edG.Text.pos,SHORT(long));
      IF edG.Text.pos < 0 THEN
        edG.Text.pos := 0
      ELSIF edG.Text.pos > edG.MaxLineLength-1 THEN
        edG.Text.pos := edG.MaxLineLength-1;
      END;
    ELSE
      edG.Text.pos := SHORT(long)-1;  (* intern bei Null anfangen *)
    END;
    edD.TextSync(FALSE);
  ELSE
    edG.Rc := edE.cmdFailed; edL.Title(edG.BadArgument);
  END;
END dGotoCol;

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

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

PROCEDURE dPage*;
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(TRUE);
END dPage;

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

PROCEDURE dPing*;
VAR
  long: LONGINT;
  (* $ClearVars- *)
BEGIN
  (* $ClearVars= *)
  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 dPing;


PROCEDURE dPong*;
VAR
  long: LONGINT;
  (* $ClearVars- *)
BEGIN
  (* $ClearVars= *)
  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);
    s.SETREG(0,I.ActivateWindow(edG.Text.win));
    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 dPong;


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


BEGIN
  (* ClearPingPongs; *)
  PagePctg := 80;
END EdMovement.
