(* ------------------------------------------------------------------------
  :Module.        ScreenSupport
  :Contents.      Special routines for intuition screens.
  :Author.        Stefan Hellwig
  :Address.       Oberer Kirchweg 12
  :Address.       3501 Schauenburg 1
  :Address.       Federal Republic of Germany
  :EMail.         Z-Netz : STEFAN_HELLWIG@URANUS.ZER
  :EMail.         UseNet : osgo.ks.he.schule.de!uranus.zer.sub.org!
                           stefan_hellwig
  :History.       V37.1 29-Aug-92
  :Copyright.     (c) 1992 by Stefan Hellwig. Public Domain.
  :Copyright.     Part of the ZOC module library !
  :Language.      Oberon
  :Translator.    AMIGA OBERON v2.14d
  :Remark.        AmigaOS 2.04 and above only !
  ------------------------------------------------------------------------ *)

MODULE ScreenSupport;

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

IMPORT
   E : Exec,
   S : SYSTEM,
   U : Utility,
   I : Intuition,
   G : Graphics;

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

TYPE
   VisibleAreaPtr * = POINTER TO VisibleArea;

   VisibleArea   * = STRUCT
         top     * : INTEGER;
         left    * : INTEGER;
         width   * : INTEGER;
         height  * : INTEGER;
   END;

   NameType      * = ARRAY I.maxPubScreenName OF CHAR;
   NameTypePtr   * = POINTER TO NameType;
   PenArrayType  * = ARRAY I.numDRIPens OF INTEGER;

   StdTagType    * = U.Tags16;
   StdTagTypePtr * = POINTER TO StdTagType;

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

CONST
     (* Standard-OS 2.x Screen-Daten; z.B. PenArray *)

     StdPenArray    = PenArrayType(-1,0,0,0,0,0,0,0,0);
     StdScrName   * = "PublicScreen";
     StdScrModeID * = G.hiresKey;

     (* Standard-Tags eines "normalen" Screens *)

     StdTags * = StdTagType(I.saHeight, I.stdScreenHeight,
                            I.saWidth, I.stdScreenWidth,
                            I.saDetailPen, I.autoFrontPen,
                            I.saBlockPen, I.autoBackPen,
                            I.saTitle, NIL,
                            I.saDepth, 2,
                            I.saPens, S.ADR(StdPenArray),
                            I.saPubName, NIL,
                            I.saPubSig, 0,
                            I.saDisplayID, NIL,
                            I.saErrorCode, NIL,
                            I.saOverscan, I.oScanText,
                            I.saShowTitle, I.LTRUE,
                            I.saSysFont, 1,
                            U.ignore, NIL,         (* fuer Erweiterungen *)
                            U.done, NIL);

      (* passende Indizes fuer diese Std-Tags *)

      IdxHeight   * = 0;
      IdxWidth    * = 1;
      IdxDetail   * = 2;
      IdxBlock    * = 3;
      IdxTitle    * = 4;
      IdxDepth    * = 5;
      IdxPens     * = 6;
      IdxPubName  * = 7;
      IdxPubSig   * = 8;
      IdxDisplay  * = 9;
      IdxError    * = 10;
      IdxOverscan * = 11;
      IdxShow     * = 12;
      IdxSysFont  * = 13;
      IdxLink     * = 14;

      (* Standard-Mindestwerte fuer GetVisibleArea() *)

      StdMinWinHeight * = 50;
      StdMinWinWidth  * = 100;

(* ------ Einige kleine Support-Prozeduren fuer GetVisibleArea() ------ *)

PROCEDURE Minimal * (a,b : INTEGER):INTEGER;
BEGIN
   IF a <= b THEN
      RETURN a;
   ELSE
      RETURN b;
   END;
END Minimal;

PROCEDURE Maximal * (a,b : INTEGER):INTEGER;
BEGIN
   IF a > b THEN
      RETURN a;
   ELSE
      RETURN b;
   END;
END Maximal;

(* --- "Veroeffentlichung" eines Screens --- *)

PROCEDURE ScrPublic * (Screen : I.ScreenPtr):BOOLEAN;
VAR
   Res : SET;
BEGIN
   Res := I.PubScreenStatus(Screen, SET{});
   IF (1 IN Res) THEN
      RETURN FALSE;
   ELSE
      RETURN TRUE;
   END;
END ScrPublic;

(* --- "Privatisierung" eines Screens --- *)

PROCEDURE ScrPrivate * (Screen : I.ScreenPtr):BOOLEAN;
VAR
   Res : SET;
BEGIN
   Res := I.PubScreenStatus(Screen, SET{I.psnfPrivate});
   IF (1 IN Res) THEN
      RETURN FALSE;
   ELSE
      RETURN TRUE;
   END;
END ScrPrivate;

(* --- Bestimmten Screen als default public screen setzen --- *)

PROCEDURE DefPubScr * (Name : NameType):BOOLEAN;
VAR
   ScrList : E.ListPtr;
   ScrNode : I.PubScreenNodePtr;
BEGIN
   ScrList := I.LockPubScreenList();
   ScrNode := ScrList.head;
   LOOP
      IF ScrNode = NIL THEN EXIT END;
      IF ScrNode.node.name^ = Name THEN
         I.SetDefaultPubScreen(Name);
         I.UnlockPubScreenList();
         RETURN TRUE;
      ELSE
         ScrNode := ScrNode.node.succ;
      END;
   END;
   I.UnlockPubScreenList();
   RETURN FALSE;
END DefPubScr;

(* ----------- Oeffnen eines Standard-Public-Screens -------------- *)

(* Tags = Tag-Array mit Screen-Einstellungen                        *)
(* Name = Name des zu oeffnenden Screens                            *)
(* DisplayID = Aufloesung des Screens (z.B. "hiresKey")             *)
(* Depth = Tiefe des Screens. Bis zu 8 Bit zulaessig                *)
(* Error = Variable zum Ablegen eines sekundaeren Error-Codes       *)
(*         fuer weitere Fehler-Analyse, wenn Screen = NIL ist.      *)
(*         Unter Umstaenden auch Rueckgabewert von ModeNotAvailable *)

PROCEDURE OpenPubScr * (VAR Tags  : ARRAY OF U.TagItem;
                        Name      : NameType;
                        DisplayID : LONGINT;
                        Depth     : LONGINT;
                        Error     : LONGINT) : I.ScreenPtr;
VAR
   Scr : I.ScreenPtr;
   Err : LONGINT;
BEGIN
   (* Test, ob DisplayID-Mode auf dem System zur Verfuegung steht *)
   Err := G.ModeNotAvailable(DisplayID);
   IF Err # 0 THEN
      Tags[IdxError].data := Err;
      RETURN NIL;
   END;

   Tags[IdxPubSig].data := E.AllocSignal(-1);
   IF Tags[IdxPubSig].data = -1 THEN RETURN NIL END;

   Tags[IdxPubName].data := S.ADR(Name);
   Tags[IdxDisplay].data := DisplayID;
   Tags[IdxDepth].data   := Depth;
   Tags[IdxError].data   := Error;

   Scr := I.OpenScreenTagListA(NIL, Tags);
   IF Scr = NIL THEN
      E.FreeSignal(Tags[IdxPubSig].data);
      RETURN NIL;
   END;

   IF NOT ScrPublic(Scr) THEN
      IF I.CloseScreen(Scr) THEN END;
      E.FreeSignal(Tags[IdxPubSig].data);
      RETURN NIL;
   END;
   RETURN Scr;
END OpenPubScr;

(* --- Schliessen des Public-Screens und Rueckgabe des Signals --- *)

(* Tags = Tag-Array mit Screen-Einstellungen                       *)
(* Screen = Zu schliessender Screen; NIL ist nicht gefaehrlich     *)

PROCEDURE ClosePubScr * (VAR Tags   : ARRAY OF U.TagItem;
                         VAR Screen : I.ScreenPtr):BOOLEAN;
BEGIN
   IF Screen#NIL THEN
      IF Tags[IdxPubSig].data # -1 THEN
         E.FreeSignal(Tags[IdxPubSig].data);
      END;
      IF ScrPrivate(Screen) THEN
         RETURN(I.CloseScreen(Screen));
      END;
   END;
END ClosePubScr;

(* -- Warten auf Signal, dass letztes Window geschlossen wurde -- *)
(* -- Danach dann eventuelles Schliessen des Screens           -- *)

(* Tags = Tag-Array wie bei Open/Close zur Erkennung d. Signals   *)
(* Screen = Zu benutzender (public) screen; evtl. NIL (s.u.) !    *)
(* CloseIt = Boolean, ob Screen geschlossen werden soll, hier     *)
(*           muss dann der Screen als Parameter angegeben werden  *)

PROCEDURE WaitPubScr * (Tags : ARRAY OF U.TagItem; Screen : I.ScreenPtr;
                        CloseIt : BOOLEAN);
VAR
   Signals : LONGSET;
   ScrSig  : INTEGER;
BEGIN
     ScrSig := SHORT(S.VAL(LONGINT, Tags[IdxPubSig].data));

     LOOP
          REPEAT
               Signals := E.Wait(LONGSET{ScrSig});
          UNTIL Signals#LONGSET{};

          IF (ScrSig IN Signals) THEN
               EXIT;
          END;
     END;
     IF CloseIt THEN
        IF ClosePubScr(Tags, Screen) THEN END;
     END;
END WaitPubScr;

(* --- Ermittlung des aktuell sichtbaren Screen-Bereichs --- *)

(* Scr = gewuenschter zu untersuchender Screen               *)
(* MinWinHeight/Width = Mindestausmasse eines evtl. Windows  *)

PROCEDURE GetVisibleArea * (Scr          : I.ScreenPtr;
                            MinWinHeight : INTEGER;
                            MinWinWidth  : INTEGER):VisibleAreaPtr;
VAR
   ScreenModeID : LONGINT;
   Area         : VisibleAreaPtr;
   Rect         : G.Rectangle;
BEGIN
   NEW(Area);
   IF (Area # NIL) AND (Scr # NIL) THEN

     (* this technique returns the text overscan rectangle of the screen that we
     ** are opening on. If you really need the actual value set into the display
     ** clip of the screen, use the VideoControl() command of the graphics lib
     ** to return a copy of the ViewPortExtra structure. See the Graphics
     ** library chapter and Autodocs for more details.
     *)
   
     ScreenModeID := G.GetVPModeID(S.ADR(Scr.viewPort));
     IF (ScreenModeID # G.invalidID) THEN
   
       IF (I.QueryOverscan(ScreenModeID, Rect, I.oScanText)#NIL) THEN
   
           (* make sure window coordinates are positive or zero *)
           Area.left := Maximal(0, -Scr.leftEdge);
           Area.top  := Maximal(0, -Scr.topEdge);
   
           (* get width and height from size of display clip *)
           Area.width := Rect.maxX - Rect.minX + 1;
           Area.height := Rect.maxY - Rect.minY + 1;
   
           (* adjust height for pulled-down screen (only show visible part) *)
           IF (Scr.topEdge > 0) THEN
              Area.height := Area.height - Scr.topEdge;
           END;
   
           (* insure that window fits on screen *)
           Area.height := Minimal(Area.height, Scr.height);
           Area.width  := Minimal(Area.width, Scr.width);
   
           (* make sure window is at least minimum size *)
           Area.width  := Maximal(Area.width, MinWinWidth);
           Area.height := Maximal(Area.height, MinWinHeight);
         END;
      END;
   END;
   RETURN Area;
END GetVisibleArea;

(* --- "symmetrische" Ergaenzung zu GetVisibleArea() --- *)

(* Area = Freizugebendes VisibleArea; NIL legal          *)

PROCEDURE FreeVisibleArea * (VAR Area : VisibleAreaPtr);
BEGIN
   IF Area # NIL THEN
      DISPOSE(Area);
   END;
END FreeVisibleArea;

(* --- Gibt Abmessungen eines zentrierten Windows als Area zurueck --- *)

(* VisArea = VisibleArea eines Screens (von GetVisibleArea())          *)
(* Height  = Hoehe des zu zentrierenden Windows                        *)
(* Width   = Breite des zu zentrierenden Windows                       *)
(* Rueckgabe der Koordinaten des Windows in einem VisibleArea          *)

PROCEDURE CenterWindow * (VisArea : VisibleAreaPtr;
                          Height  : INTEGER;
                          Width   : INTEGER):VisibleAreaPtr;
VAR
   Area : VisibleAreaPtr;
BEGIN
   NEW(Area);
   IF (Area # NIL) AND (VisArea # NIL) THEN
      Area.top    := (VisArea.height DIV 2) - (Height DIV 2) + VisArea.top;
      Area.left   := (VisArea.width DIV 2) - (Width DIV 2) + VisArea.left;
      Area.height := Height;
      Area.width  := Width;
   END;
   RETURN(Area);
END CenterWindow;

END ScreenSupport.


