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

:Program.    EdDisplay.mod
:Contents.   Screen- and Gadget-Routines for AmokEd
:Author.     Hartmut Goebel
:Language.   Oberon
:Translator. Amiga Oberon Compiler V2.13
:Imports.    SupLib (Hartmut Goebel)
:History.    V0.1 03 Dec 1990, Hartmut Goebel [hG]
:History.    V1.0 02 Apr 1991 [hG]
:Date.       08 Feb 1992 01:23:05

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

MODULE EdDisplay;

IMPORT
  e  : Exec,
  edE: EdErrors,
  edG: EdGlobalVars,
  edL: EdLowLevel,
  g  : Graphics,
  I  : Intuition,
  lst: EdLists,
  ol : OberonLib,
  sl : SupLib,
  str: Strings,
  s  : SYSTEM;

CONST
  NoPropFonts = "No Proportional Fonts!!";
  UnableToFindFont = "Unable to find font";
  ResizeWindow = "Please Resize Window";

PROCEDURE ColT* (n{0}: INTEGER): INTEGER;
  BEGIN RETURN (edG.XTBase + n * edG.XSize); END ColT;
PROCEDURE RowT* (n{0}: INTEGER): INTEGER;
  BEGIN RETURN (edG.YTBase + n * edG.YSize); END RowT;
PROCEDURE Col* (n{0}: INTEGER): INTEGER;
  BEGIN RETURN (edG.XBase + n * edG.XSize); END Col;
PROCEDURE Row* (n{0}: INTEGER): INTEGER;
  BEGIN RETURN (edG.YBase + n * edG.YSize); END Row;


PROCEDURE SetPen*(Line: LONGINT);
VAR
  Pen: SHORTINT;
  (* $ClearVars- *)
BEGIN
  (* $ClearVars= *)
  Pen := 1;
  IF (edG.Text = edG.Block.Owner) AND (Line>=edG.Block.SNum)
  AND (Line<=edG.Block.ENum) AND ~(edG.commLineMode IN edG.Status) THEN
    Pen := 2;
  END;
  IF (edG.Config.screenDepth = 1)
  OR ((edG.Config.screenDepth = 0) & (edG.WBScreenDepth = 1)) THEN
    g.SetAPen(edG.RPort,1);
    IF Pen = 1 THEN
      g.SetDrMd(edG.RPort,g.jam2);
    ELSE
      g.SetDrMd(edG.RPort,SHORTSET{g.inversvid}+g.jam2);
    END;
  ELSIF Pen # edG.RPort.fgPen THEN
    g.SetAPen(edG.RPort,Pen);
  END;
END SetPen;


PROCEDURE TextCursor*(on:BOOLEAN);
VAR
  buf: e.STRPTR;
  px, py: INTEGER;
  inBlock: BOOLEAN;
  (* $ClearVars- *)
BEGIN
  (* $ClearVars= *)
  IF ~(edG.noCursor IN edG.Status) AND ~(edG.iconMode IN edG.Text.status) THEN
    edL.MoveToCursor;
    inBlock := (edG.Text=edG.Block.Owner) AND (edG.Text.line>=edG.Block.SNum)
             AND (edG.Text.line<=edG.Block.ENum);
    g.SetAPen(edG.RPort,1);
    IF on THEN
      g.SetDrMd(edG.RPort,SHORTSET{g.complement});
      px := Col(edG.Text.pos-edG.Text.topPos);
      py := Row(SHORT(edG.Text.line-edG.Text.topLine));
      IF edG.insertMode IN edG.Text.status THEN
        g.RectFill(edG.RPort,px,py,px+edG.XSize-1,py+edG.YSize-1);
      ELSE
        g.RectFill(edG.RPort,px,py+(edG.YSize DIV 2),px+edG.XSize-1,py+edG.YSize-1);
      END;
    ELSE
      g.SetDrMd(edG.RPort,g.jam2);
      IF inBlock AND (edG.Text.pos<=edG.LineBufferLen)
      AND (edG.Config.screenDepth = 1) THEN
        g.SetDrMd(edG.RPort,SHORTSET{g.inversvid}+g.jam2);
      END;
      IF inBlock AND (edG.Config.screenDepth # 1)
      AND NOT(edG.commLineMode IN edG.Status) THEN
        g.SetAPen(edG.RPort,2); END;
      buf := s.ADR(edG.LineBuffer[edG.Text.pos]);
      IF buf[0] # 0X THEN
        g.Text(edG.RPort,buf^,1);
      ELSE
        g.Text(edG.RPort,edG.Spaces,1);
      END;
    END;
    g.SetDrMd(edG.RPort,g.jam2);
  END;
END TextCursor;

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

PROCEDURE SetWindowParams*;
VAR
 win: I.WindowPtr;
  (* $ClearVars- *)
BEGIN
  (* $ClearVars= *)
  win := edG.Text.win;
  edG.RPort := win.rPort;
  edG.XSize := edG.RPort.font.xSize;

  edG.YSize := edG.RPort.font.ySize;
  edG.XBase := win.borderLeft;
  edG.YBase := win.borderTop;
  edG.XPixs := win.width-win.borderRight-win.borderLeft;
  edG.YPixs := win.height-win.borderBottom-win.borderTop;
  IF edG.kick20 IN edG.Status THEN
    DEC(edG.XPixs); DEC(edG.YPixs);
  END;
  edG.Columns := edG.XPixs DIV edG.XSize;
  edG.Rows := edG.YPixs DIV edG.YSize;
  edG.XTBase := edG.XBase;
  edG.YTBase := edG.YBase+edG.RPort.font.baseline;
  IF NOT (edG.iconMode IN edG.Text.status) THEN
    edG.SetProp; END;
END SetWindowParams;

(*----------------------------------------------------------------------*)
(*
 * bringt ab <start> <n> Zeilen auf den Bildschirm, hiezu müssen
 * edG.Text.topLinePtr und edG.Text.actLinePtr den aktuellen Wert haben
 *)

PROCEDURE TextDisplaySeg*(start, n: INTEGER);
VAR
  i, c: INTEGER;
  ptr: e.STRPTR;
  thisLine: edG.LinePtr;
  (* $ClearVars- *)
BEGIN
  (* $ClearVars= *)
  IF edG.NoScreenUpdate > 0 THEN RETURN; END;
  IF start < 0 THEN
    INC(n,start); start := 0;
  END;
  thisLine := edG.Text.topLinePtr;
  IF NOT (edG.commLineMode IN edG.Status) THEN
    lst.GoForwardNil(thisLine,start); END;
  i := start;
  WHILE (i < start+n) AND (i < edG.Rows) AND (thisLine#NIL) DO
    IF edG.commLineMode IN edG.Status THEN
      ptr := s.ADR(edG.LineBuffer);
      g.SetAPen(edG.RPort,1);
      i := edG.Rows-1;
    ELSIF (thisLine = edG.Text.actLinePtr) THEN
      ptr := s.ADR(edG.LineBuffer);
      SetPen(edG.Text.line);
    ELSE
      ptr := thisLine(edG.Line).string;
      SetPen(edG.Text.topLine+i);
    END; (* IF *)
    c := edL.Length(ptr^)-edG.Text.topPos;
    IF c > 0 THEN
      ptr := s.ADR(ptr[edG.Text.topPos]);
      g.Move(edG.RPort,ColT(0),RowT(i));
      IF c > edG.Columns THEN c := edG.Columns; END;
      g.Text(edG.RPort,ptr^,c);
    END;
    INC(i);
    thisLine := thisLine.next(edG.Line);
  END (* WHILE *)
END TextDisplaySeg;


PROCEDURE TextRedisplay*;
BEGIN
  IF edG.NoScreenUpdate > 0 THEN
    RETURN; END;
  g.SetAPen(edG.RPort,0);
  IF edG.commLineMode IN edG.Status THEN
    g.RectFill(edG.RPort,Col(0),Row(edG.Rows-1),
               edG.XBase+edG.XPixs,edG.YBase+edG.YPixs);
  ELSE
    g.RectFill(edG.RPort, edG.XBase, edG.YBase,
               edG.XBase+edG.XPixs, edG.YBase+edG.YPixs);
  END;
  TextDisplaySeg(0,edG.Rows);
  edG.SetProp; (* eben nicht SetPropKnob!*)
END TextRedisplay;


PROCEDURE TextRedisplayCurrentLine*;
VAR
  row: INTEGER;
  (* $ClearVars- *)
BEGIN
  (* $ClearVars= *)
  IF edG.NoScreenUpdate > 0 THEN
    RETURN; END;
  row := SHORT(edG.Text.line-edG.Text.topLine);
  g.SetAPen(edG.RPort,0);
  g.RectFill(edG.RPort, Col(0), Row(row),
             edG.XBase+edG.XPixs, Row(row+1)-1);
  TextDisplaySeg(row,1);
END TextRedisplayCurrentLine;

(*-----------------------------------------------------------------------*)
(*
 * Syncronisiert den Bildschirm mit dem Text bezüglich der aktuellen
 * Werte edG.Text.line, edG.Text.pos und edG.Text.actLinePtr
 * Anwendung: line, pos und actLinePtr setzten, TextSync aufrufen
 *)

PROCEDURE TextSync*(redraw: BOOLEAN);
BEGIN
  IF (edG.Text.pos >= edG.Text.topPos + edG.Columns)
  OR (edG.Text.pos < edG.Text.topPos) THEN
    redraw := TRUE;
    edG.Text.topPos := edG.Text.pos - edG.Columns DIV 2;
    IF edG.Text.topPos < 0 THEN edG.Text.topPos := 0; END;
  END;
  IF (edG.Text.line >= edG.Text.topLine + edG.Rows)
  OR (edG.Text.line < edG.Text.topLine) THEN
    redraw := TRUE;
    edG.Text.topLine := edG.Text.line - edG.Rows DIV 2;
    IF edG.Text.topLine < 0 THEN edG.Text.topLine := 0; END;
    edG.Text.topLinePtr := edG.Text.actLinePtr;
    lst.GoBackwardNil(edG.Text.topLinePtr,edG.Text.line-edG.Text.topLine);
  END;
  edL.FillUpSpaces;
  IF redraw THEN
    INCL(edG.Status,edG.alreadyRedrawn);
    TextRedisplay;
  ELSE
    EXCL(edG.Status,edG.alreadyRedrawn);
  END;
END TextSync;


PROCEDURE TextSyncHorizontal*();
BEGIN
  IF (edG.Text.pos >= edG.Text.topPos + edG.Columns)
  OR (edG.Text.pos < edG.Text.topPos) THEN
   edG.Text.topPos := edG.Text.pos - edG.Columns DIV 2;
   IF edG.Text.topPos < 0 THEN edG.Text.topPos := 0; END;
  END;
END TextSyncHorizontal;


(*------------------------------------------------------------------------*)
(*
 * Kopiert den ZeilenPuffer nach Entfernen der Spaces
 * am Zeilenende in die Aktuelle Zeile
 *)

PROCEDURE PutBackLine*;
VAR
  thisLine: edG.LinePtr;
  ptr: e.STRPTR;
  len, neededSpace : INTEGER;
BEGIN
  edL.StripEndSpaces;
  len := edG.LineBufferLen;
  IF NOT (edG.commLineMode IN edG.Status) THEN
    thisLine := edG.Text.actLinePtr;
    WITH thisLine:edG.Line DO
      IF (thisLine.string^ # edG.LineBuffer) THEN
        INCL(edG.Text.status,edG.modified);
        EXCL(edG.Text.status,edG.quit); (* explizit: sicher ist sicher! *)
        neededSpace := len+edG.ChunkSize-(len MOD edG.ChunkSize);
        IF neededSpace # thisLine.len THEN (* anderer Platzbedarf? *)
          ptr := e.AllocMem(neededSpace,LONGSET{});
          IF ptr # NIL THEN
            e.FreeMem(thisLine.string,thisLine.len);
            thisLine.string := ptr;
            thisLine.len := neededSpace;
          ELSE
            INCL(edG.Status,edG.memoryFail);
            edG.LineBufferLen := thisLine.len;
            e.CopyMemQuick(edG.LineBuffer,thisLine.string^,edG.LineBufferLen);
            thisLine.string[edG.LineBufferLen] := 0X;
            RETURN;
          END; (* IF ptr # NIL *)
        END; (* IF neededSpace # thisLine.len *)
        e.CopyMemQuick(edG.LineBuffer,thisLine.string^,neededSpace);
      END; (* IF thisLine.string^ # edG.LineBuffer *)
    END; (* WITH *)
  END; (* NOT commLineMode *)
END PutBackLine;


(*
 * Holt aktuelle Zeile in den ZeilenPuffer
 *)

PROCEDURE TextLoad*;
BEGIN
  IF edG.commLineMode IN edG.Status THEN RETURN; END;
  e.CopyMemQuick(edG.Text.actLinePtr(edG.Line).string^,edG.LineBuffer,
                 edG.Text.actLinePtr(edG.Line).len);
  edG.LineBufferLen := edL.Length(edG.LineBuffer);
  edL.FillUpSpaces;
END TextLoad;

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

PROCEDURE TextPosition*(col, row: INTEGER);
BEGIN
  PutBackLine;
  IF col = 0 THEN
    col  := -1; END;
  edG.Text.pos := edG.Text.topPos+col;
  IF edG.Text.pos > edG.MaxLineLength-2 THEN
    edG.Text.pos := edG.MaxLineLength-2;
  ELSIF edG.Text.pos < 0 THEN
    edG.Text.pos := 0
  END;
  edG.Text.line := edG.Text.topLine+row;
  edG.Text.actLinePtr := edG.Text.topLinePtr;
  IF row > 0 THEN
    IF edG.Text.line >= edG.Text.numberOfLines THEN
      edG.Text.line := edG.Text.numberOfLines-1; END;
    lst.GoForwardNil(edG.Text.actLinePtr,edG.Text.line-edG.Text.topLine);
  ELSIF row < 0 THEN
    lst.GoBackwardNil(edG.Text.actLinePtr,-row);
    IF edG.Text.line <= 0 THEN
      edG.Text.actLinePtr := edG.Text.lineList.head;
      edG.Text.line := 0;
    END;
  END;
  TextLoad;
  TextSync(FALSE);
END TextPosition;

(*----------------------------------------------------------------------*)
(*
 * Schaltet Editior auf <txt> um
 *)

PROCEDURE SwitchEdit*(txt: edG.TextHeaderPtr);
BEGIN
  PutBackLine;
  IF NOT (edG.iconMode IN edG.Text.status) THEN
    edG.Rc := edE.cmdInitial;
    edL.WindowTitle;
  END;
  edG.Text := txt;
  SetWindowParams;
  TextLoad;
  IF NOT (edG.iconMode IN edG.Text.status) THEN TextSync(FALSE); END;
  (*edL.WindowTitle;*)
END SwitchEdit;

(*----------------------------------------------------------------------*)
(*
 * patch to speed up scrolling
 *)

PROCEDURE FastScrollRaster*(dx,dy,l,t,w,h: INTEGER);
VAR
  depth: POINTER TO BYTE;
  old: BYTE;
  (* $ClearVars- *)
BEGIN
  (* $ClearVars= *)
   depth := s.ADR(edG.RPort.bitMap.depth);
   old := depth^;

   e.Forbid();
   IF (edG.Block.SNum > edG.Text.topLine + edG.Rows)
   OR (edG.Block.ENum < edG.Text.topLine - 1) THEN
      depth^ := 1;
   END;
   g.ScrollRaster(edG.RPort,dx,dy,l,t,w,h);
   depth^ := old;
   e.Permit();
END FastScrollRaster;

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

PROCEDURE dSetFont*;
VAR
  font: g.TextFontPtr;
  win: I.WindowPtr;
  size: LONGINT;
  minHeight, topOffset: INTEGER;
  (* $ClearVars- *)
BEGIN
  (* $ClearVars= *)
  win := edG.Text.win;
  IF NOT edL.StrToInt(edG.Arg[1],size) OR (size > MAX(INTEGER)) THEN
    edL.Title(edG.BadArgument); edG.Rc := edE.cmdError;
    RETURN;
  END;
  font := sl.GetFont(edG.Arg[0]^,SHORT(size));
  IF font # NIL THEN
    IF g.proportional IN font.flags THEN
      g.CloseFont(font);
      edL.Title(NoPropFonts); edG.Rc := edE.cmdFailed;
      RETURN;
    END;
    IF edG.Text.font # NIL THEN
      g.CloseFont(edG.Text.font); END;
    edG.Text.font := font;
    g.SetFont(win.rPort,font);
    g.SetRast(win.rPort,0);

    topOffset := edG.Screen.wBorTop+font.ySize+1; (* Top border offset *)
    minHeight := topOffset + edG.MinHeightOffset;
    IF win.minHeight < minHeight THEN
      edL.Title(ResizeWindow); edG.Rc := edE.cmdValid2;
      (*
      IF win.height < minHeight THEN
        I.MoveWindow(win,-win.leftEdge,-win.topEdge);
        I.SizeWindow(win,minHeight,0);
      END;
      IF I.WindowLimits(win,0,minHeight,0,0) THEN END;
      *)
    END;
    I.RefreshWindowFrame(win);
    SetWindowParams;
    TextSync(TRUE);
  ELSE
    edL.Title(UnableToFindFont); edG.Rc := edE.cmdFailed;
  END;
END dSetFont;

END EdDisplay.

