(* ------------------------------------------------------------------------
   :Program.      Menu.
   :Contents.     Proceduren zum Erstellen und Abfragen von Menüs.
   :Author.       Klaus Hlawaty.
   :Address.      Waldhof
   :Address.      3579 Schrecksbach
   :History.      v1.0 - 06.Sep.91
   :History.      v1.1 - 20.Nov.91
   :History.      v1.2 - 23.Nov.91: FmtMenu.
   :Copyright.    Freeware.
   :Language.     Oberon.
   :Translator.   OBERON v2.13 / OLink v2.13d
   :Imports.      Speed.
   :Imports.      xDisplay.
   :Imports.      xIntui.
   :Remarks.      Benutzt erweiterte RECORDs und STRUCT's.
   :Remarks.      Erweitert die 'MenuItem'-Struktur von Intuition um eine
   :Remarks.        Kennung. Diese kann zum änderungsunabhängigen Abfrage
   :Remarks.        von Menüpunkten benutzt werden.
   :Remarks.      Erweitert das 'Window'-Record von Display um einige
   :Remarks.        Pointer zum Aufbau und Verwaltung von Menü's.
   :Remarks.        Ein Window muß also mit dem 'WinMenu'-Record und
   :Remarks.        der Procedure 'OpenWinMenu' aus Menu aufgebaut und mit
   :Remarks.        'CloseWinMenu' aus Menu geschlossen werden.
   :Remarks.        Auf das Window können aber alle Display-Proceduren
   :Remarks.        und alle Möglichkeiten von xDisplay angewandt werden
   :Remarks.      Benutzt die MODULE: speed, xDisplay, xIntui.
------------------------------------------------------------------------ *)
MODULE Menu;

IMPORT
      e : Exec,
      D : Display,
     xD : xDisplay,
      g : Graphics,
      i : Intuition,
     xi : xIntui,
     ng : Requests,
          NoGuruRq,
     ol : OberonLib,
          Speed,
    str : Strings,
      s : SYSTEM;

TYPE
    MItem*      = STRUCT(MI : i.MenuItem)
                    Kennung* : INTEGER;
                  END (* STRUCT *);

    MItemPtr*   = POINTER TO MItem;

    WinMenu*    =  RECORD(D.Window)             (* Erweitertes Window *)
                     LastMenu* : i.MenuPtr;     (* Diese Zeiger werden für *)
                     LastItem* : i.MenuItemPtr; (* die ADD-Befehle         *)
                     LastSub*  : i.MenuItemPtr; (* benötigt.               *)
                     Menu*     : i.MenuPtr;
                   END (* WinMenu *);

    WinMenuPtr* = POINTER TO WinMenu;

VAR


(* ======================================================================= *)
(* ============= Öffnen und Schliessen von Fenstern für Menü's =========== *)
(* ======================================================================= *)

(*------  Open:  ------*)

(* $CopyArrays- *)
PROCEDURE OpenWinMenu*(win2:    WinMenuPtr;
                       title:   ARRAY OF CHAR;
                       x,y,w,h: INTEGER;
                       screen:  i.ScreenPtr): BOOLEAN;

(* ------------------------------------------------------------------------
  :Input.     win2    : Zeiger auf die erweiterte WindowStructur.
  :Input.     title   : Titel des Windows.
  :Input.     x,y     : Linker oberer Eckpunkt.
  :Input.     w       : Breite des Window's.
  :Input.     h       : Höhe des Window's.
  :Input.     screen  : Öffnen auf screen / NIL => Workbench.
  :Output.    RETURN  : Wenn geöffnet dann TRUE.
  :Semantic.  Öffnet Window, mit erweitertem Windowstructur für "Menu".
------------------------------------------------------------------------ *)
BEGIN
  win2.LastMenu := NIL;
  win2.LastItem := NIL;
  win2.LastSub  := NIL;
  RETURN xD.OpenWindow(win2,title,x,y,w,h,screen);
END OpenWinMenu;

(*-------------------------------  Close:  ------------------------------*)

PROCEDURE KillItem(I : i.MenuItemPtr);
(* ------------------------------------------------------------------------
  :Input.     I : MenüItemPointer.
  :Semantic.  Speicherrückgabe von Item's und SubItems.
  :Remarks.   rekursiver Aufruf.
------------------------------------------------------------------------ *)
BEGIN
  IF(I # NIL)THEN
    KillItem(I.subItem);    (* Kill Subitem's *)
    KillItem(I.nextItem);
    DISPOSE(I);
  END (* I # NIL *);
END KillItem;

PROCEDURE KillMenu(m : i.MenuPtr);
(* ------------------------------------------------------------------------
  :Input.     I : Menüpointer.
  :Semantic.  Speicherrückgabe von Menü's.
  :Remarks.   rekursiver Aufruf.
------------------------------------------------------------------------ *)
BEGIN
  IF(m # NIL)THEN
    KillItem(m.firstItem);
    KillMenu(m.nextMenu);
    DISPOSE(m);
  END (* m # NIL *);
END KillMenu;

PROCEDURE Close*(d: D.DispElPtr);
(* ------------------------------------------------------------------------
   :Input.     d  : Zeiger auf D.Screen, D.Window oder WinMenu-Record.
   :Semantic.  Schließt Menü und Window.
------------------------------------------------------------------------ *)

BEGIN
  IF d IS WinMenu THEN
    KillMenu(d(WinMenu).Menu);
  END (* IF d IS WinMenu *);
  D.Close(d);
END Close;

(* ======================================================================= *)
(* ================ Proceduren zum Installieren von Menü's =============== *)
(* ======================================================================= *)

(* $CopyArrays- *)
PROCEDURE AddMenu*(VAR win      : WinMenuPtr;
                       Text     : ARRAY OF CHAR);
(* ------------------------------------------------------------------------
   :Input.    win       : Zeiger auf das Window.
   :Input.    Text      : Text des Menüs.
   :Output.   win       : Über die WinMenuStruktur wird das Menü angehängt.
   :Semantic. Addiert Menü.
------------------------------------------------------------------------ *)
VAR
    Menu : i.MenuPtr;

BEGIN
  ng.Assert(win # NIL
           ,"AddMenu: Window => NIL, zuerst OpenWinMenu aufrufen");
  NEW(Menu);
    ng.Assert(Menu # NIL, "AddMenu: Kein RAM für Menu");
    xi.FillMenu(Menu,Text,0,0);
    IF(win.LastMenu # NIL)THEN
      win.LastMenu^.nextMenu := Menu;   (* Altes Menu -> Neues *)
    ELSE
      win.Menu               := Menu;
    END (* IF win *);
    win.LastMenu := Menu;
    win.LastItem := NIL;
    win.LastSub  := NIL;
END AddMenu;


(* $CopyArrays- *)
PROCEDURE AddItem*(win      : WinMenuPtr;
                   Text     : ARRAY OF CHAR;
                   Command  : CHAR;
                   Kennung  : INTEGER
                   )        : i.MenuItemPtr;
(* ------------------------------------------------------------------------
   :Input.    win       : Zeiger auf den Window.
   :Input.    Text      : Text des Menü-Items.
   :Input.    Command   : ShortCut.
   :Input.    Kennung   : Kennung # 0.
   :Output.   win       : Über den WinMenu wird das Item angehängt.
   :Output.   RETURN    : Item Adresse für Änderungen.
   :Semantic. Addiert Item, die Adresse wird für e.v. nachträgliche
   :Semantic. Änderungen zurücküberreicht.
   :Remark.   leftEdge  : Setzen durch FmtMenu.
   :Remark.   topEdge   : Setzen durch FmtMenu.
   :Remark.   width     : Setzen durch FmtMenu.
   :Remark.   height    : Setzen durch FmtMenu.
   :Remark.   MutualEx  : Setzen durch MutualE.
   :Remark.   Flags     : Werden vorbesetzt, Änderung siehe RETURN.
------------------------------------------------------------------------ *)
VAR
    item  : MItemPtr;

BEGIN
  ng.Assert(win # NIL
           ,"AddItem: Window => NIL, zuerst OpenWinMenu aufrufen");
  ng.Assert(win.LastMenu # NIL
           ,"AddItem: LastMenu => NIL, zuerst AddMenu aufrufen");
  NEW(item);
    ng.Assert(item # NIL, "AddItem: Kein RAM für Item");
    xi.FillItem(item,
                Text,
                0,
                0,
                0,
                0,
                SET{i.itemText,i.highComp,i.itemEnabled},
                LONGSET{},
                Command);
    item.Kennung := Kennung;
    win.LastSub := NIL;
    IF   (win.LastItem # NIL)THEN
      win.LastItem^.nextItem  := item;
      win.LastItem            := item;
    ELSIF(win.LastMenu # NIL)THEN
      win.LastMenu^.firstItem := item;
      win.LastItem            := item;
    END (* ELSIF *);
  RETURN  item;
END AddItem;
(* $CopyArrays- *)

(* $CopyArrays- *)
PROCEDURE AddTItem*(win     : WinMenuPtr;
                   Text     : ARRAY OF CHAR;
                   Command  : CHAR;
                   Kennung  : INTEGER;
                   Checked  : BOOLEAN
                   )        : i.MenuItemPtr;
(* ------------------------------------------------------------------------
   :Input.    win       : Zeiger auf den Window.
   :Input.    Text      : Text des Menü-Items.
   :Input.    Command   : ShortCut.
   :Input.    Kennung   : Kennung # 0.
   :Input.    Checked   : TRUE = aktiviert / FALSE = deaktiviert.
   :Output.   win       : Über den WinMenu wird das Item angehängt.
   :Output.   RETURN    : Item Adresse zur nachträglichen Änderung.
   :Semantic. Addiert Toggle-Item, die Adresse wird für e.v. nachträgliche
   :Semantic. Änderungen zurücküberreicht.
   :Remark.   leftEdge  : Setzen durch FmtMenu.
   :Remark.   topEdge   : Setzen durch FmtMenu.
   :Remark.   width     : Setzen durch FmtMenu.
   :Remark.   height    : Setzen durch FmtMenu.
   :Remark.   MutualEx  : Setzen durch MutualEx oder direkt siehe RETURN.
   :Remark.   Flags     : Werden vorbesetzt, Änderung siehe RETURN.
------------------------------------------------------------------------ *)
VAR
    item  : i.MenuItemPtr;

BEGIN
    item := AddItem(win,Text,Command,Kennung);
    item.flags := item.flags + SET{i.checkIt,i.menuToggle,i.checked};
    IF NOT(Checked) THEN
      EXCL(item.flags,i.checked);
    END (* IF *);
  RETURN item;
END AddTItem;

PROCEDURE AddSeparator*(win : WinMenuPtr): i.MenuItemPtr;
(* ------------------------------------------------------------------------
   :Input.    win    : Zeiger auf den Window.
   :Output.   win    : Über den WinMenu wird das Item angehängt.
   :Output.   RETURN : Item Adresse.
   :Semantic. Erzeugt eine Trennlinie im MenÜ.
------------------------------------------------------------------------ *)
VAR
    Item : i.MenuItemPtr;
    iText : i.IntuiTextPtr;
    Str : e.STRPTR;

BEGIN
  Item := AddItem(win
                 ,"-"
                 ,"\o"
                 ,0000); (* Spezifiziert Separator *)
  iText := Item.itemFill;
  Str   := iText.iText;
  Speed.Fill(0,s.SIZE(Str^),Str^,Speed.Byte); (* Löscht String gründlich *)
  Item.flags := SET{i.itemText};
  RETURN Item;
END AddSeparator;

(* $CopyArrays- *)
PROCEDURE AddSubI*(win      : WinMenuPtr;
                   Text     : ARRAY OF CHAR;
                   Command  : CHAR;
                   Kennung  : INTEGER
                   )        : i.MenuItemPtr;
(* ------------------------------------------------------------------------
   :Input.    MenuHead  : Zeiger auf den Window.
   :Input.    Text      : Text des Menü-Items.
   :Input.    Command   : ShortCut.
   :Input.    Kennung   : Kennung # 0.
   :Output.   win       : Über den WinMenu wird das Item angehängt.
   :Output.   RETURN    : SubItem Adresse für spätere Änderungen.
   :Semantic. Addiert SubItem, die Adresse wird für e.v. nachträgliche
   :Semantic. Änderungen zurücküberreicht.
   :Remark.   leftEdge  : Setzen durch FmtMenu.
   :Remark.   topEdge   : Setzen durch FmtMenu.
   :Remark.   width     : Setzen durch FmtMenu.
   :Remark.   height    : Setzen durch FmtMenu.
   :Remark.   MutualEx  : Setzen durch MutualEx oder direkt siehe RETURN.
   :Remark.   Flags     : Werden vorbesetzt, Änderung siehe RETURN.
------------------------------------------------------------------------ *)
VAR
    item  : MItemPtr;

BEGIN
  ng.Assert(win # NIL
           ,"AddSubI: Window => NIL, zuerst OpenWinMenu aufrufen");
  ng.Assert(win.LastMenu # NIL
           ,"AddSubI: LastMenu => NIL, zuerst AddMenu aufrufen");
  ng.Assert(win.LastItem # NIL
           ,"AddSubI: LastItem => NIL, zuerst AddItem aufrufen");
  NEW(item);
    ng.Assert(item # NIL, "AddSubI: Kein RAM für SubItem");
    xi.FillItem(item,
                Text,
                0,
                0,
                0,
                0,
                SET{i.itemText,i.highComp,i.itemEnabled},
                LONGSET{},
                Command);
    item.Kennung := Kennung;
    IF   (win.LastSub # NIL)THEN
      win.LastSub^.nextItem  := item;
      win.LastSub            := item;
    ELSIF(win.LastItem # NIL)THEN
      win.LastItem^.subItem := item;
      win.LastSub           := item;
    END (* ELSIF *);
  RETURN  item;
END AddSubI;

PROCEDURE AddTSubI*(win      : WinMenuPtr;
                    Text     : ARRAY OF CHAR;
                    Command  : CHAR;
                    Kennung  : INTEGER;
                    Checked  : BOOLEAN
                    )        : i.MenuItemPtr;
(* ------------------------------------------------------------------------
   :Input.    win       : Zeiger auf den Window.
   :Input.    Text      : Text des Menü-Items.
   :Input.    Command   : ShortCut.
   :Input.    Kennung   : Kennung # 0.
   :Input.    Checked   : TRUE = aktiviert / FALSE = deaktiviert.
   :Output.   win       : Über den WinMenu wird das Item angehängt.
   :Output.   RETURN    : SubItem Adresse zur nachträglichen Änderung.
   :Semantic. Addiert Toggle-Item, die Adresse wird für e.v. nachträgliche
   :Semantic. Änderungen zurücküberreicht.
   :Remark.   leftEdge  : Setzen durch FmtMenu.
   :Remark.   topEdge   : Setzen durch FmtMenu.
   :Remark.   width     : Setzen durch FmtMenu.
   :Remark.   height    : Setzen durch FmtMenu.
   :Remark.   MutualEx  : Setzen durch MutualEx oder direkt siehe RETURN.
   :Remark.   Flags     : Werden vorbesetzt, Änderung siehe RETURN.
------------------------------------------------------------------------ *)
VAR
    item  : i.MenuItemPtr;

BEGIN
    item := AddSubI(win,Text,Command,Kennung);
    item.flags := item.flags + SET{i.checkIt,i.menuToggle,i.checked};
    IF NOT(Checked) THEN
      EXCL(item.flags,i.checked);
    END (* IF *);
  RETURN item;
END AddTSubI;

PROCEDURE AddSubSeparator*(win : WinMenuPtr): i.MenuItemPtr;
(* ------------------------------------------------------------------------
   :Input.    MenuHead  : Zeiger auf den Window.
   :Output.   win       : Über den WinMenu wird das Item angehängt.
   :Output.   RETURN    : Item Adresse.
   :Semantic. Erzeugt eine Trennlinie im Sub-MenÜ.
------------------------------------------------------------------------ *)
VAR
    Item : i.MenuItemPtr;
    iText : i.IntuiTextPtr;
    Str : e.STRPTR;

BEGIN
  Item := AddSubI(win
                 ,"-"
                 ,"\o"
                 ,0000); (* Spezifiziert Separator *)
  iText := Item.itemFill;
  Str   := iText.iText;
  Speed.Fill(0,s.SIZE(Str^),Str^,Speed.Byte); (* Löscht String gründlich *)
  Item.flags := SET{i.itemText};
  RETURN Item;
END AddSubSeparator;

(* ======================================================================= *)
(* =================== Proceduren zum Umgang von Menü's ================== *)
(* ======================================================================= *)

PROCEDURE KennToItem*(win   : WinMenuPtr;
                      Kenn  : INTEGER) : i.MenuItemPtr;
(* ------------------------------------------------------------------------
   :Input.    win    : Zeiger auf das Window.
   :Input.    Kenn   : Überreicht Kennung.
   :Output.   RETURN : Menuadresse | NIL => Kein Menu.
   :Semantic. Sucht ItemAdresse aus Menu über "Kennung".
------------------------------------------------------------------------ *)
VAR
    Menu     : i.MenuPtr;
    Item     : i.MenuItemPtr;
    Gefunden : BOOLEAN;

BEGIN
  Gefunden := FALSE;
  Menu     := win.Menu;
  WHILE((Menu # NIL)AND NOT(Gefunden))DO
    Item := Menu.firstItem;
    WHILE((Item # NIL)AND NOT(Gefunden))DO
      Gefunden := Item(MItem).Kennung = Kenn;
      IF NOT(Gefunden)THEN Item := Item.nextItem; END;
    END (* WHILE Item *);
    Menu := Menu.nextMenu;
  END (* WHILE Menu *);
  RETURN Item;
END KennToItem;

PROCEDURE ItemAdr*(win   : WinMenuPtr;
                   code  : INTEGER) : i.MenuItemPtr;
(* ------------------------------------------------------------------------
   :Input.    win    : Zeiger auf das Window.
   :Input.    code   : Überreicht Messagecode.
   :Output.   RETURN : Menuadresse | NIL => Kein Menu.
   :Semantic. Sucht ItemAdresse aus Menu über "code".
   :Remarks.  Funkioniert auch mit Untermenü's.
------------------------------------------------------------------------ *)
VAR
    nMenu,
    nItem,
    nSubI : INTEGER;
    Menu  : i.MenuPtr;
    Item  : i.MenuItemPtr;

BEGIN
  IF(code # -1)THEN
    xi.decodeMenu(code,nMenu,nItem,nSubI);
      Menu := win.Menu;
      WHILE (nMenu # 0)AND(Menu # NIL) DO
        Menu := Menu.nextMenu;
        DEC(nMenu);
      END (* WHILE *);
      IF(Menu # NIL)THEN
        Item := Menu.firstItem;
        WHILE (nItem # 0)AND(Item # NIL) DO
          Item := Item.nextItem;
          DEC(nItem);
        END (* WHILE *);
        IF(Item # NIL)THEN
          IF(Item.subItem # NIL)THEN
            Item := Item.subItem;
            WHILE nSubI # 0 DO
              Item := Item.nextItem;
              DEC(nSubI);
            END (* WHILE *);
          END (* IF subItem *);
          RETURN Item;
        END (* IF Item # NIL *);
      END   (* IF Menu # NIL *);
  END (* IF code # -1 *);
  RETURN NIL;                                 (* Kein Menu *)
END ItemAdr;

PROCEDURE Wait*(    win    : WinMenuPtr;
                VAR class  : LONGSET;
                VAR code   : INTEGER;
                VAR iadr   : s.ADDRESS) : i.MenuItemPtr;
(* ------------------------------------------------------------------------
  :Input.     win    : Zeiger auf das Window.
  :Output.    class  : Überreicht Messageclass zurück.
  :Output.    code   : Überreicht Messagecode  zurück.
  :Output.    iadr   : Überreicht IAddress     zurück.
  :Output.    RETURN : MenuItem-Pointer | NIL => Kein Menü.
  :Semantic.  Wartet auf Intuitionmessage und werte e.v. Menü's aus.
------------------------------------------------------------------------ *)
VAR
    msg   : i.IntuiMessagePtr;

BEGIN
  e.WaitPort(win.window.userPort);
  msg := e.GetMsg(win.window.userPort);
  class := msg.class;
  code  := msg.code;
  iadr  := msg.iAddress;
  e.ReplyMsg(msg);
  IF(i.menuPick IN class)THEN
    RETURN ItemAdr(win,code);
  END;
  RETURN NIL; (* Kein Menü *)
END Wait;

PROCEDURE MenuON*  (win : WinMenuPtr);
(* ------------------------------------------------------------------------
   :Input.    win    : Zeiger auf das Window.
   :Semantic. Schaltet Menü ein.
------------------------------------------------------------------------ *)
BEGIN
  IF(win IS WinMenu)THEN
    ng.Assert(i.SetMenuStrip(win.window,win(WinMenu).Menu^)
                            ,"MenuON: Menü nicht installiert");
  END (* IF win IS WinMenu *);
END MenuON;

PROCEDURE MenuOFF* (win : WinMenuPtr);
(* ------------------------------------------------------------------------
   :Input.    win    : Zeiger auf das Window.
   :Semantic. Schaltet Menü aus.
------------------------------------------------------------------------ *)
BEGIN
  IF(win IS WinMenu)THEN
    IF(win.window.menuStrip # NIL)THEN
      i.ClearMenuStrip(win.window);
    END;
  END (* IF win IS WinMenu *);
END MenuOFF;

PROCEDURE ItemON*  (win  : WinMenuPtr;
                    Kenn : INTEGER);
(* ------------------------------------------------------------------------
   :Input.    win  : Zeiger auf das Window.
   :Input.    Kenn : Kennung des anzuschaltenden Item's.
   :Semantic. Schaltet Item ein.
   :Remarks.  Verändert Item.flags.
------------------------------------------------------------------------ *)
VAR
    Item : i.MenuItemPtr;

BEGIN
  IF(win IS WinMenu)THEN
    Item := KennToItem(win,Kenn);
    IF(Item # NIL)THEN
      INCL(Item.flags,i.itemEnabled);
    END (* IF *);
  END (* IF win IS WinMenu *);
END ItemON;

PROCEDURE ItemOFF*(win  : WinMenuPtr;
                   Kenn : INTEGER);
(* ------------------------------------------------------------------------
   :Input.    win  : Zeiger auf das Window.
   :Input.    Kenn : Kennung des anzuschaltenden Item's.
   :Semantic. Schaltet Item aus.
   :Remarks.  Verändert Item.flags.
------------------------------------------------------------------------ *)
VAR
    Item : i.MenuItemPtr;

BEGIN
  IF(win IS WinMenu)THEN
    Item := KennToItem(win,Kenn);
    IF(Item # NIL)THEN
      EXCL(Item.flags,i.itemEnabled);
    END (* IF *);
  END (* IF win IS WinMenu *);
END ItemOFF;

(* ======================================================================= *)
(* ==================== Formatierung ===================================== *)
(* ======================================================================= *)

PROCEDURE SearchBig(    Item : i.MenuItemPtr;
                    VAR B    : INTEGER;
                    VAR Ins  : BOOLEAN     );
(* ------------------------------------------------------------------------
  :Input.     Item : ItemPointer.
  :I/O.       B    : Breite des Textes.
  :I/O.       Ins  : Einfügen zweier Blank's.
  :Semantic.  Sucht rekursiv nach größtem Textbreite.
------------------------------------------------------------------------ *)
VAR
    iText : i.IntuiTextPtr;
    Str : e.STRPTR;
    B2  : INTEGER;

BEGIN
  IF(Item(MItem).Kennung # 0)THEN
    iText := Item.itemFill;
    Str   := iText.iText;
    IF(Ins)AND(Str^[0] # " ")AND(Str^[1] # " ")THEN
      str.Insert(Str^,0,"  ");
    END (* IF checkIt *);
    B2 := str.Length(Str^) * 10;
    IF(i.commSeq IN Item.flags)THEN
      B2 := B2 + 3 * 10;
    END (* IF commSeq *);
    IF(B < B2)THEN B := B2; END;
  END (* IF kein Separator *);
  IF(Item.nextItem # NIL)THEN SearchBig(Item.nextItem,B,Ins); END;
END SearchBig;

PROCEDURE SetWert(Item : i.MenuItemPtr;
                  Xb,
                  B,
                  Yb,
                  H    : INTEGER);
(* ------------------------------------------------------------------------
  :I/O.       Item : ItemPointer, Werte werden verändert.
  :Input.     Xb   : Linker Begin des Textes.
  :Input.     B    : Breite des Textes.
  :Input.     Yb   : Oberer Begin des Textes.
  :Input.     H    : Höhe des Textes.
  :Semantic.  Setzte der Werte.
------------------------------------------------------------------------ *)
VAR
    iText : i.IntuiTextPtr;
    Str : e.STRPTR;

BEGIN
  IF(Item(MItem).Kennung = 0)THEN
    iText := Item.itemFill;
    Str   := iText.iText;
    Speed.FillC("-",(B DIV 10)+1,Str^);
  END (* IF Separator *);
  Item.leftEdge := Xb;
  Item.topEdge  := Yb;
  Item.width    := B;
  Item.height   := H;
  Item := Item.nextItem;
  IF(Item # NIL)THEN SetWert(Item,Xb,B,Yb+H,H); END;
END SetWert;

PROCEDURE FmtItem(Item : i.MenuItemPtr;
                  Xb   : INTEGER     );

(* ------------------------------------------------------------------------
  :I/O.       Item : ItemPointer, Werte werden verändert.
  :Input.     Xb   : Begin des Textes.
  :Semantic.  Formatiert die Itemzeile.
------------------------------------------------------------------------ *)
VAR
    lfItem : i.MenuItemPtr;
    Xe     : INTEGER;
    Ins    : BOOLEAN;

BEGIN
  Ins    := FALSE;
  lfItem := Item;
  WHILE(lfItem # NIL)DO
    Ins := Ins OR (i.checkIt IN lfItem.flags);
    lfItem := lfItem.nextItem;
  END (* WHILE *);
  SearchBig(Item,Xe,Ins);
  SetWert(Item,Xb,Xe,1,10);
  WHILE(Item # NIL)DO
    IF(Item.subItem # NIL)THEN FmtItem(Item.subItem,Xe); END;
    Item := Item.nextItem;
  END (* WHILE *);
END FmtItem;

PROCEDURE FmtMenu*(win : WinMenuPtr);
(* ------------------------------------------------------------------------
  :I/O.       win    : Zeiger auf das Window.
  :Output.    Verändert die Inhalte des Menü-Baumes.
  :Semantic.  Formatiert die Menü's.
  :Remarks.   Diese Procedure sollte nach dem Aufbau des Menü's
  :Remarks.   und vor dem Einschalten mit MenuON aufgerufen werden !!!
------------------------------------------------------------------------ *)
VAR
    Menu : i.MenuPtr;
    Von,
    Len  : INTEGER;

BEGIN
  Von := 0;
  Menu := win.Menu;
  WHILE(Menu # NIL)DO
    FmtItem(Menu.firstItem,0);
    Menu.leftEdge := Von;
    Len := str.Length(Menu.menuName^) * 10 + 10;
    Menu.width    := Len;
    Von := Von + Len;
    Menu := Menu.nextMenu;
  END (* While *);
END FmtMenu;

(* ======================================================================= *)
(* ===================== MutualExclude =================================== *)
(* ======================================================================= *)

PROCEDURE MutualEx*(win   : WinMenuPtr;
                    Kenn1,
                    Kenn2 : INTEGER;
                    Beide : BOOLEAN);
(* ------------------------------------------------------------------------
  :Input.     win   : Zeiger auf das Window.
  :Input.     Kenn1 : diese Kennung schließt
  :Input.     Kenn2 : diese Kennung aus.
  :Input.     Beide : Beide Item's schließen sich gegenseitig aus.
  :Output.    Verändert die Inhalte der beiden Items.
  :Semantic.  Item von Kenn1 schließt Item von Kenn2 (oder beide) aus.
  :Remarks.   Diese Procedure sollte nach dem Aufbau des Itemzweig's
  :Remarks.   aufgerufen werden !!!
------------------------------------------------------------------------ *)
VAR
    Item,
    Item1,
    Item2 : i.MenuItemPtr;
    Nr,
    Nr1,
    Nr2   : INTEGER;

BEGIN
  Item1 := NIL;
  Item2 := NIL;
  ng.Assert(win.LastMenu # NIL
           ,"MutualEx: Menü nicht installiert!");
  IF   (win.LastSub  # NIL)THEN Item := win.LastItem.subItem;
  ELSIF(win.LastItem # NIL)THEN Item := win.LastMenu.firstItem;
                                Item := NIL;
  END (* Item oder SubItem *);
  Nr := 0;
  WHILE(Item # NIL)DO
    IF(Item(MItem).Kennung = Kenn1)THEN
      Nr1   := Nr;
      Item1 := Item;
    ELSIF(Item(MItem).Kennung = Kenn2)THEN
      Nr2   := Nr;
      Item2 := Item;
    END (* 1. oder 2. Kennung *);
    INC(Nr);
    Item := Item.nextItem;
  END (* WHILE *);
  ng.Assert((Item1 # NIL)AND(Item2 # NIL)
           ,"MutualEx: mindestens eine Kennung nicht gefunden!");
  INCL(Item1.mutualExclude,Nr2);
  IF(Beide)THEN
    INCL(Item2.mutualExclude,Nr1);
  END (* IF Beide *);
END MutualEx;

(* ======================================================================= *)
(* ======================================================================= *)
(* ======================================================================= *)

END Menu.

