IMPLEMENTATION MODULE Display;

(******************************************************************************)
(*                                                                            *)
(*  Modul:     Display                Version:            1.00                *)
(*                                                                            *)
(*  Datum:     27.01.1989             Author:             D.I. Vinzenz Hiter  *)
(*                                                                            *)
(*  Hardware:  Commodore AMIGA 500    Betriebssystem:     AMIGA-DOS V 1.2     *)
(*                                                                            *)
(*  Sprache:   ETH MODULA-2           Entwicklungssystem: M2AMIGA, © AMSoft   *)
(*                                                                            *)
(*  Compiler:  M2C V 3.2d             Linker:             M2L V 3.2d          *)
(*                                                                            *)
(*                                                                            *)
(*  Prozeduren:                                                               *)
(*  ------------------------------------------------------------------------  *)
(*  AutoRequ            Komfortabler Autorequester                            *)
(*  AlertMsg            Alerttexte                                            *)
(*  SetAutoRequWindow   Weist der AutoRequ ein neues Ausgabefenster zu        *)
(*                                                                            *)
(******************************************************************************)

(*---------------------- Import aus Standardbibliothek -----------------------*)

FROM SYSTEM        IMPORT ADR;
FROM Strings       IMPORT Copy, Insert, Delete, Length;
FROM Heap          IMPORT AllocMem, Deallocate;
FROM InOut         IMPORT WriteString, WriteLn;

(*---------------------- Import aus Amigabibliothek --------------------------*)
FROM Graphics      IMPORT jam1, jam2;
FROM Exec          IMPORT CopyMem;
FROM Intuition     IMPORT IDCMPFlagSet, IntuiText, IntuiTextPtr, WindowPtr,
                          vanillaKey, AutoRequest, BuildSysRequest,
                          FreeSysRequest, PrintIText, IntuiTextLength,
                          DisplayAlert;

(*---------------------- Import aus eigener Bibliothek -----------------------*)
FROM ExtStrings1   IMPORT String, StringPtr;
(*
  Typendeklarationen für Stringbehandlung

TYPE
   String    = ARRAY [0..255] OF CHAR;
   StringPtr = POINTER TO ARRAY [0..255] OF CHAR;
*)

(*---------------------- globale Definitionen --------------------------------*)
CONST Seperator = '|';

VAR autoRequWindPtr : WindowPtr;

(******************************************************************************)
(*                                                                            *)
(*  SetAutoRequWindow (windPtr);                                              *)
(*                                                                            *)
(*  Setzen eines WindowPtr für die AutoRequFunktion.                          *)
(*  In vielen Funktionen die allgemein verwendet werden sind oft Ausgaben     *)
(*  von Texten nötig. Die Funktionen kennen aber ihre Fensterumgebung, in der *)
(*  sie laufen, nicht. Wenn z.B. Applikationen eigene Screens aufmachen sind  *)
(*  Meldungen am Workbenchscreen zu vermeiden.                                *)
(*  Mit diser Funktionen kann man einen allgemeinen WindowPtr der AutoRequ    *)
(*  zur Verfügung stellen, der immer dann verwendet wird wenn dort der        *)
(*  WindowPtr NIL ist.                                                        *)
(*                                                                            *)
(*----------------------------------------------------------------------------*)
(*  Versorgung:                                                               *)
(*  windPtr: WindowPtr;     Window dem der Requester zugeordnet wird          *)
(*                                                                            *)
(******************************************************************************)
PROCEDURE SetAutoRequWindow (windPtr: WindowPtr);

BEGIN
  autoRequWindPtr := windPtr;
END SetAutoRequWindow;


(*----------------------------------------------------------------------------*)
PROCEDURE GetMinWidth (tPtr, pPtr, nPtr: IntuiTextPtr): INTEGER;
(*----------------------------------------------------------------------------*)
VAR max,  len: INTEGER;

BEGIN
  max := 0;
  (* Textlenge für BodyText *)
  WHILE tPtr # NIL DO
    len := IntuiTextLength (tPtr);
    IF max < len THEN max := len; END;
    tPtr := tPtr^.nextText;
  END;
  INC (max, 40);                  (* linken und rechten Rand addieren *)

  (* Textlenge für positiv und negativ Text *)
  len := 120;    (* linker, rechter Rand + Zwischenraum *)
  IF pPtr # NIL THEN INC (len, IntuiTextLength (pPtr)); END;
  IF nPtr # NIL THEN INC (len, IntuiTextLength (nPtr)); END;
  IF max < len THEN max := len; END;
  RETURN max;
END GetMinWidth;

(*.pa*)
(******************************************************************************)
(*                                                                            *)
(*  negHit = AutoRequ (windPtr, bodyText, posText, negText): BOOLEAN;         *)
(*                                                                            *)
(*  Anzeige eines Requesters mit automatischer Größenberechnung entsprechend  *)
(*  dem angezeigten Text. Der Text kann mehrzeilig sein. Als Zeilentrenner    *)
(*  wird im String das Zeichen '|' verwendet.                                 *)
(*  Der Requester kann auch mit einer beliebigen Taste quittiert werden.      *)
(*  Sind positive und negative Texte vorhanden wird damit negativ quittiert.  *)
(*                                                                            *)
(*----------------------------------------------------------------------------*)
(*  Versorgung:                                                               *)
(*  windPtr: WindowPtr;     Window dem der Requester zugeordnet wird          *)
(*                          Für windPtr = NIL erscheint der Req. am Workbscr. *)
(*  bodyTextPtr: StringPtr; Requesterinhalt mit Zeilentrenner                 *)
(*  posTextPtr:  StringPtr; Einzeiliger Text im positiv zu quit. Gadget.      *)
(*                          Kann entfallen durch Übergabe von NIL             *)
(*  negTextPtr:  StringPtr; Einzeiliger Text im negativ zu quit. Gadget       *)
(*                          Muß unbedingt vorhanden sein !                    *)
(*                                                                            *)
(*  Ergebnis:                                                                 *)
(*  negHit: BOOLEAN;    = TRUE wenn negativ quittiert wurde.                  *)
(*                                                                            *)
(******************************************************************************)
PROCEDURE AutoRequ (windPtr: WindowPtr; bodyTextPtr, posTextPtr, negTextPtr:
                    StringPtr):BOOLEAN;

VAR bodyITextPtr, posITextPtr, negITextPtr: IntuiTextPtr;
    textPtr, lastTextPtr:                   IntuiTextPtr;
    i, lines, width, height : INTEGER;

CONST leftOffset = 10;
      topOffset  = 5;
      lineOffset = 10;
      unknown    = " ????? ";

(*----------------------------------------------------------------------------*)
PROCEDURE MakeIntuiText (VAR intuiText: IntuiTextPtr; left, top: INTEGER;
                         textPtr: StringPtr): BOOLEAN;
(* Dynamische Allokation und Initialisation der IntuiText Struktur            *)
(*----------------------------------------------------------------------------*)

BEGIN
  AllocMem (intuiText, SIZE(IntuiText), FALSE); (* Allokation im FAST Memory *)
  IF intuiText # NIL THEN                     (* genug Memory              *)
    WITH intuiText^ DO
      frontPen  := 0;
      backPen   := 2;
      drawMode  := jam1;
      leftEdge  := left;
      topEdge   := top;
      iTextFont := NIL;
      iText     := textPtr;
      nextText  := NIL;
    END;
    RETURN TRUE;
  END;
  RETURN FALSE;
END MakeIntuiText;


(*----------------------------------------------------------------------------*)
PROCEDURE FreeRequ ();
(* Freigeben der dynamisch Allokierten Bereiche *)
(*----------------------------------------------------------------------------*)

VAR helpPtr: IntuiTextPtr;

BEGIN
  WHILE bodyITextPtr # NIL DO
    helpPtr := bodyITextPtr^.nextText;
    Deallocate (bodyITextPtr);
    bodyITextPtr := helpPtr;
  END;
  Deallocate (posITextPtr);
  Deallocate (negITextPtr);
END FreeRequ;

(*----------------------------------------------------------------------------*)
PROCEDURE FailedRequ () : BOOLEAN;
(* Umwandlung des Requesters in ein Alert bei zu geringem Speicher *)
(*----------------------------------------------------------------------------*)

BEGIN
  FreeRequ ();
  WriteString ("Zu wenig Speicher für die Anzeige"); WriteLn;
  RETURN FALSE;
END FailedRequ;

(*-------------------------- Haupprogramm ------------------------------------*)
BEGIN
  bodyITextPtr := NIL;
  posITextPtr  := NIL;
  negITextPtr  := NIL;

  IF negTextPtr = NIL THEN (* Versorungsfehler; muß belegt sein *)
    negTextPtr := ADR(unknown);
  END;

  (* Start der IntuiTextstruktur *)
  IF NOT MakeIntuiText (bodyITextPtr, leftOffset, topOffset, bodyTextPtr)
  THEN  RETURN FailedRequ (); END;

  (* Weitere Zeilenauftrennung *)
  lines := 1;
  lastTextPtr := bodyITextPtr;
  FOR i := 0 TO Length(bodyTextPtr^)  DO
    IF bodyTextPtr^[i] = Seperator THEN           (* Trenner gefunden *)
      bodyTextPtr^[i] := CHAR(0);                 (* Ersetzen durch Terminator *)
      IF NOT MakeIntuiText (textPtr, leftOffset, topOffset + lines*lineOffset,
                            StringPtr(ADR(bodyTextPtr^[i]) + 1))
      THEN  RETURN FailedRequ (); END;
      lastTextPtr^.nextText := textPtr;
      lastTextPtr   := textPtr;
      INC(lines);
  END; END;

  IF posTextPtr = NIL THEN posITextPtr := NIL;
  ELSE
    IF NOT MakeIntuiText (posITextPtr, 6, 3, posTextPtr) THEN
      RETURN FailedRequ ();
    END;
  END;

  (* negativ Text kann nicht fehlen *)
  IF NOT MakeIntuiText (negITextPtr, 6, 3, negTextPtr) THEN
      RETURN FailedRequ ();
  END;

  width  := GetMinWidth (bodyITextPtr, posITextPtr, negITextPtr);
  height := 2*topOffset + lines*lineOffset + 35;

  IF windPtr # NIL THEN windPtr := autoRequWindPtr; END;
  RETURN AutoRequest (windPtr, bodyITextPtr, posITextPtr, negITextPtr,
                      IDCMPFlagSet{}, IDCMPFlagSet{vanillaKey}, width, height);
  FreeRequ ();
END AutoRequ;


(*.pa*)
(******************************************************************************)
(*                                                                            *)
(*  AlertMsg (msgText)                                                        *)
(*                                                                            *)
(*  Anzeige eines Alerts mit automatischer Größenberechnung entsprechend      *)
(*  dem angezeigten Text. Der Text kann mehrzeilig sein. Als Zeilentrenner    *)
(*  wird im String das Zeichen '|' verwendet.                                 *)
(*  Der Alert ist ein 'Recoverable Alert' und führt für zu keinem ReBoot.     *)
(*                                                                            *)
(*----------------------------------------------------------------------------*)
(*                                                                            *)
(*  Versorgung:                                                               *)
(*  msgTextPtr: StringPtr;  Text für den Alert.                               *)
(*              Wird sofort mit einem Zeilentrenner (z.B. "|Fehler .. "),     *)
(*              NullString ("") oder NIL Pointer begonnen                     *)
(*              so steht als erste Zeile des Alerts folgender Standardtext:   *)
(*              "Software error. Press...."                                   *)
(*                                                                            *)
(******************************************************************************)
PROCEDURE AlertMsg (msgTextPtr: StringPtr);
(*


   Alertstruktur:

   +-----+ = 1 Byte
                                                              Zeilenverkettung
                                                                        |
                +------+------+-----+------------| |------+--------+---------+
 Alertzeile 1   |  x Position |y Pos|   Text              | CHAR(0)| CHAR(1) |
                +------+------+-----+------------| |------+--------+---------+
                +------+------+-----+------------| |------+--------+---------+
 Alertzeile 2   |  x Position |y Pos|   Text              | CHAR(0)| CHAR(1) |
                +------+------+-----+------------| |------+--------+---------+
                .......
                +------+------+-----+------------| |------+--------+---------+
 Letzte Zeile   |  x Position |y Pos|   Text              | CHAR(0)| CHAR(0) |
                +------+------+-----+------------| |------+--------+---------+

*)


CONST CleftEdge   = 30;
      CLineInc    = 15;
      CMaxLineChar= 70;
      CHeaderLine = "Software failure. Press left or right mouse button to continue.";
      ATRecovery  = 000000000H;       (* Alertnummer *)
      ATDeadEnd   = 080000000H;

(*---------------------------------------------------------------------------*)
(*   Lokale Prozedur für das Einfügen von Textzeilen entsprechend der
     Amiga DisplayAlert Struktur
*)
PROCEDURE InsertMsg (no: INTEGER; VAR curPos: INTEGER;
                     VAR alertText: ARRAY OF CHAR; newTextPtr: StringPtr);
(*---------------------------------------------------------------------------*)

VAR newLen: INTEGER;

BEGIN
  newLen := Length (newTextPtr^);                 (* Überprüfung der Textlänge *)
  IF newLen > CMaxLineChar THEN
    newLen := CMaxLineChar;
    newTextPtr^ [newLen] := CHAR(0);
  END;
  alertText [curPos]   := CHAR (0);               (* HighByte der x Position *)
  alertText [curPos+1] := CHAR (CleftEdge);       (* LowByte  der x Position *)
  alertText [curPos+2] := CHAR(CLineInc * no);    (* y Position              *)
  CopyMem (newTextPtr, ADR(alertText) + curPos +3, newLen + 1);
  curPos := curPos + newLen + 5;                  (* neuer Längenzeiger      *)
  alertText [curPos -1] := CHAR (1);         (* Markierung für nächsten Text *)
END InsertMsg;

(* ===================== Hauptprocedure ==================================== *)
VAR alertText, helpText : String;              (* Puffer für Alerttext       *)
    lineNo,                                    (* Aktuelle Zeile             *)
    curPos,                                    (* Längenzeiger für CopyMem   *)
    i, k : INTEGER;                            (* Schleifenzähler            *)

BEGIN
  curPos     := 0;
  lineNo     := 0;
  alertText  := "";
  IF msgTextPtr = NIL THEN                            (* Standardmeldung *)
    INC (lineNo);
    InsertMsg (lineNo, curPos, alertText, ADR(CHeaderLine));
  ELSIF Length (msgTextPtr^) = 0 THEN
    INC (lineNo);
    InsertMsg (lineNo, curPos, alertText, ADR(CHeaderLine));
  ELSE
    k := 0;
    IF msgTextPtr^[0] = Seperator THEN
      INC (lineNo);
      InsertMsg (lineNo, curPos, alertText, ADR(CHeaderLine));  (* Standardmeldung *)
      k := 1;
    END;
    (* Hole Textteile zwischen den Seperatoren und erzeuge Alertstring *)
    FOR i := 1 TO Length (msgTextPtr^) DO
      IF (msgTextPtr^[i] = Seperator) OR (msgTextPtr^[i] = CHAR(0))THEN
        INC(lineNo);
        Copy (helpText, msgTextPtr^, k, i - k);
        InsertMsg (lineNo, curPos, alertText, ADR(helpText));
        k := i + 1;
      END;
    END;
  END;
  alertText[curPos-1] := CHAR(0);            (* Keine Zeile folgt mehr *)
  IF DisplayAlert(ATRecovery, ADR(alertText), lineNo * CLineInc +10)  THEN
  END;
END AlertMsg;

BEGIN
  autoRequWindPtr := NIL;
END Display.


