IMPLEMENTATION MODULE RESOURCE ;
(*$LargeVars := FALSE *)
(*$LongAlign := TRUE *)
(* - Dateiübergabe per Kommandozeilenparameter *)
(* - Ton bei Fehler *)
IMPORT DosL , ExecL , Windows , GfxMacros ;
IMPORT GraphicsL , Hardware , Detach , LayersL ;
IMPORT IntuiL : IntuitionL , ReqTFileReq ;
IMPORT rt : ReqTools , rts : ReqToolsSupport , String , Heap ;
IMPORT pp : PowerPacker ;
FROM IntuitionL IMPORT intuitionBase ;
FROM ExecL IMPORT execBase ;
FROM Arts IMPORT kickVersion, thisTask, returnVal, Error, BreakPoint ;
FROM Conversions IMPORT ValToStr ;
FROM SYSTEM IMPORT LONGSET, ADR, ADDRESS, CAST, TAG, ASSEMBLE ;
FROM IntuitionD   IMPORT Gadget, GadgetPtr, IntuiText, IntuiTextPtr, ExtNewScreen,
                         Screen, ScreenPtr, DrawInfo, DrawInfoPtr,
                         customScreen, oScanText, Border, BorderPtr,
                         ExtNewWindow, Window, WindowPtr, PropInfo,
                         boolGadget, IntuiMessagePtr, IntuiMessage,
                         PropInfoPtr, propGadget,
                         IDCMPFlagSet, WindowFlagSet,
                         ScreenFlagSet, ScreenFlags,
                         GadgetFlagSet, GadgetFlags,
                         ActivationFlagSet, ActivationFlags,
                         IDCMPFlags, WindowFlags, DrawPens,
                         PropInfoFlagSet, PropInfoFlags, SaTags, WaTags;
FROM ExecD        IMPORT MemReqs, MemReqSet ;
FROM GraphicsD    IMPORT ViewModes, ViewModeSet, RastPortPtr, jam1, Rectangle,
                         RectanglePtr, invalidID, RastPort, UCopListPtr,
                         ViewPortPtr, UCopList, Region, RegionPtr, DrawModes,
                         DrawModeSet, ViewPort, VTags, vtagEndCm,
                         TextExtent, TextFontPtr, TextAttr, CopListPtr,
                         FontFlags, FontFlagSet, FontStyles, FontStyleSet ;
FROM RandomNumber IMPORT Random, PutSeed;
FROM FileSystem   IMPORT Response, File, Lookup, SetPos, ReadByteBlock,
                         WriteByteBlock, WriteBytes, Close;
FROM Windows      IMPORT WinGad, WinGadSet ;
FROM InputEvent   IMPORT Qualifiers, QualifierSet ;
(*----------------------------------------------------------------------------*)

PROCEDURE DiskFehlermeldung (Fehler : Response) ;

VAR Ausgabe : LONGSTRING ;

BEGIN
 CASE Fehler
 OF
 | notdone : Ausgabe := "Fehler beim Dateizugriff."
 | lockErr : Ausgabe := "Programm konnte Datei nicht für sich reservieren." ;
 | openErr : Ausgabe := "Fehler beim Öffnen der Datei." ;
 | readErr : Ausgabe := "Es trat ein Lesefehler auf." ;
 | writeErr : Ausgabe := "Es trat ein Schreibfehler auf." ;
 | seekErr : Ausgabe := "Fehler beim Positionieren innerhalb der Datei." ;
 | memErr : Ausgabe := "Speichermangel beim Dateizugriff." ;
 | notFound : Ausgabe := "Datei konnte nicht gefunden werden." ;
 | diskWriteProtected : Ausgabe := "Das Medium ist schreibgeschützt !" ;
 | deviceNotMounted : Ausgabe := "Das angesprochene logische oder physische "+
                                          "Gerät ist nicht angemeldet." ;
 | diskFull : Ausgabe := "Das Medium ist voll!" ;
 | deleteProtected : Ausgabe := "Die Datei ist gegen Löschen geschützt." ;
 | writeProtected : Ausgabe := "Die Datei ist gegen überschreiben geschützt." ;
 | notDosDisk : Ausgabe := "Keine Dos - Diskette." ;
 | noDisk : Ausgabe := "Es wurde kein Medium eingelegt" ;
 ELSE
  Ausgabe := "Es trat ein unbekannter Fehler beim Zugriff auf den\n"+
             "Massenspeicher auf." ;
 END ; (* CASE *)
 rts.vEZRequest (ADR(Ausgabe), ADR("Weiter"), NIL, ADDRESS(RequesterTags), NIL) ;

END DiskFehlermeldung ;

PROCEDURE Fehlermeldung (Fehler : FehlerTyp) ;

VAR Ausgabe : LONGSTRING ;

BEGIN
 CASE Fehler
 OF
 |   ungScreenModeID :
      Ausgabe :=  "Es konnte keine gültige ScreenModeID\n"+
                  "geholt werden." ;
 |   Fontoeffnen : Ausgabe := "Der Font der Workbench konnte nicht\n"+
                              "geöffnet werden." ;
 |   Dateizukurz :
      Ausgabe := "Die Datei ist zu kurz !" ;
 |   Dateigesichert :
      Ausgabe := "Die Datei wurde erfolgreich gesichert!" ;
 |   Speicherleer :
      Ausgabe := "Der Vokabelspeicher ist leer !" ;
 |   PPVersion :
      Ausgabe := "Diese Datei wurde mit einer unbekannten\n"+
                 "Version der PowerPacker©® - Library\n"+
                 "erstellt." ;
 |   unbekannteDatei :
      Ausgabe := "Diese Datei wurde von einer unbekannten\n"+
                 "Programmversion erstellt !" ;
 |   DateizumSchreibenoeffnen :
      Ausgabe := "Beim Versuch, die Datei zum Schreiben zu öffnen,\n"+
                 "kam es zu einem Fehler. Die Datei wurde nicht\n" +
                 "geschrieben." ;
 |   keinRahmen :
      Ausgabe := "Speichermangel, Auswahlrahmen konnte nicht erzeugt\n"+
                 "werden." ;
 |   Speichermangel :
      Ausgabe := "Speichermangel, verstümmelte Einträge gelöscht !" ;
 |   nixFenster :
      Ausgabe := "Es konnte kein Fenster geöffnet werden." ;
 |   nixScreen  :
      Ausgabe := "Es konnte kein Screen geöffnet werden." ;
 |   falschePrefs :
      Ausgabe := "Die Einstellungsdatei ist zerstört bzw. veraltet." ;
 |   ZeigerFehler :
      Ausgabe := "Ein Zeiger ist NIL !" ;
 |   Screennichtfrei :
      Ausgabe := "Der Bildschirm ist noch von anderen Programmen in\n"+
                 "Benutzung. Bitte alle fremden Fenster schließen\n"+
                 "und dann nochmal versuchen !" ;
 |   keineScreenDaten :
      Ausgabe := "Das Programm konnte nicht an den aktuellen Screen\n"+
                 "herankommen.\n" ;
 |   Packenfehlgeschlagen :
      Ausgabe := "Packen der Daten ist fehlgeschlagen.\n"+
                 "Erneuter Versuch mit kleinerem Puffer folgt." ;
 |   PackenfehlgeschlagenEnde :
      Ausgabe := "Packen der Daten ist fehlgeschlagen.\n"+
                 "Datei wurde ungepackt gesichert." ;
 |   Datenb :
      Ausgabe := "Fehler beim Datenblockerstellen !" ;
 |   regionFehler : Ausgabe := "Region konnte nicht erzeugt werden." ;
 |   CaseIndex    : Ausgabe := "ungültiger Case-Index (interner Fehler)" ;
 ELSE
  Ausgabe := "unbekannter Fehler!" ;
 END ; (* CASE *)
 rts.vEZRequest (ADR(Ausgabe), ADR("Weiter"), NIL, ADDRESS(RequesterTags), NIL) ;
END Fehlermeldung ;

PROCEDURE InitScreenParm (VAR ScreenParm : ExtNewScreen ;
                          VAR ScrTitle   : ARRAY OF CHAR ;
                          VAR tagbuf     : tagtype;
                          VAR MyFontZ    : TextFontPtr ;
                          VAR MyFontParm : TextAttr) ;

(* Prozedur trägt Werte in ExtNewScreen - Struktur zum öffnen eines Screens ein *)

VAR Status                       : BOOLEAN ;
    ScreenDataCopy               : Screen ;
    ScreenDataCopyZ              : ScreenPtr ;
    ScreenDrawInfo               : DrawInfo ;
    ScreenDrawInfoZ              : DrawInfoPtr ;
    ScreenModeID, FontNameLength : LONGCARD ;
    ScreenFontNameZ, FontNameZ   : STRPTR ;

BEGIN
 MyFontParm.name := ADR("topaz") ;
 MyFontParm.ySize := 8 ;
 MyFontParm.style := FontStyleSet{} ;
 MyFontParm.flags := FontFlagSet{romFont} ;
 IF kickVersion >= 36 THEN
  ScreenDataCopyZ := IntuiL.LockPubScreen(NIL) ;
  IF ScreenDataCopyZ # NIL THEN
   ScreenDrawInfoZ := IntuiL.GetScreenDrawInfo (ScreenDataCopyZ) ;
   IF ScreenDrawInfoZ # NIL THEN
    ScreenParm.extension := TAG (tagbuf,
                                 saLeft,        ScreenDataCopyZ^.leftEdge,
                                 saTop,         ScreenDataCopyZ^.topEdge,
                                 saWidth,       ScreenDataCopyZ^.width,
                                 saHeight,      ScreenDataCopyZ^.height,
                                 saDepth,       ScreenDrawInfoZ^.depth,
                                 saTitle,       ADR(ScrTitle),
                                 saShowTitle,   TRUE,
                                 saPens,        ScreenDrawInfoZ^.pens,
                                 saFullPalette, TRUE,
                                 saOverscan,    oScanText,
                                 saAutoScroll,  TRUE,
                                 tagEnd) ;
    ScreenModeID := GraphicsL.GetVPModeID (ADR(ScreenDataCopyZ^.viewPort)) ;
    IF LONGINT(ScreenModeID) # invalidID
    THEN
     ScreenParm.extension := TAG (tagbuf,
                                  saLeft,        ScreenDataCopyZ^.leftEdge,
                                  saTop,         ScreenDataCopyZ^.topEdge,
                                  saWidth,       ScreenDataCopyZ^.width,
                                  saHeight,      ScreenDataCopyZ^.height,
                                  saDepth,       ScreenDrawInfoZ^.depth,
                                  saTitle,       ADR(ScrTitle),
                                  saShowTitle,   TRUE,
                                  saPens,        ScreenDrawInfoZ^.pens,
                                  saFullPalette, TRUE,
                                  saOverscan,    oScanText,
                                  saAutoScroll,  TRUE,
                                  saDisplayID,   ScreenModeID,
                                  tagEnd) ;
(*     ScreenFontNameZ := STRPTR(ScreenDrawInfoZ^.font^.message.node.name) ;
     FontNameLength := String.Length (ScreenFontNameZ^) + 1 ;
     Heap.Allocate (FontNameZ, FontNameLength) ;
     IF FontNameZ # NIL
     THEN
      String.Copy (FontNameZ^, ScreenFontNameZ^) ;
      MyFontParm.name  := FontNameZ ;
      MyFontParm.ySize := (8 * 13 + 2 + ScreenDataCopyZ^.wBorTop +
                           ScreenDataCopyZ^.wBorBottom + 1) DIV 13 ;
      IF ScreenDrawInfoZ^.font^.ySize < MyFontParm.ySize
      THEN
       MyFontParm.ySize := ScreenDrawInfoZ^.font^.ySize ;
      END ;
      MyFontParm.style := ScreenDrawInfoZ^.font^.style ;
      MyFontParm.flags := ScreenDrawInfoZ^.font^.flags ;

      MyFontZ := GraphicsL.OpenFont (ADR(MyFontParm)) ;
(*      BreakPoint (NIL) ;*)
      IF MyFontZ # NIL
      THEN
       ScreenParm.extension := TAG (tagbuf,
                                    saLeft,        ScreenDataCopyZ^.leftEdge,
                                    saTop,         ScreenDataCopyZ^.topEdge,
                                    saWidth,       ScreenDataCopyZ^.width,
                                    saHeight,      ScreenDataCopyZ^.height,
                                    saDepth,       ScreenDrawInfoZ^.depth,
                                    saTitle,       ADR(ScrTitle),
                                    saShowTitle,   TRUE,
                                    saPens,        ScreenDrawInfoZ^.pens,
                                    saFullPalette, TRUE,
                                    saOverscan,    oScanText,
                                    saAutoScroll,  TRUE,
                                    saFont,        ADR(MyFontParm),
                                    saDisplayID,   ScreenModeID,
                                    tagEnd) ;
      ELSE
       Fehlermeldung (Fontoeffnen) ;
      END ;
     ELSE
      Fehlermeldung (Speichermangel) ;
     END ; *)
    ELSE (* fehlerhafte ScreenModeID *)
     Fehlermeldung (ungScreenModeID) ;
    END ;
   END ;
   IntuiL.FreeScreenDrawInfo (ScreenDataCopyZ, ScreenDrawInfoZ) ;
   ScreenDrawInfoZ := NIL ;
   IntuiL.UnlockPubScreen (NIL, ScreenDataCopyZ) ;
  END ;
  WITH ScreenParm.ns
  DO
   leftEdge := ScreenDataCopyZ^.leftEdge ; topEdge := ScreenDataCopyZ^.topEdge ;
   width := ScreenDataCopyZ^.width ; height := ScreenDataCopyZ^.height ;
   detailPen := ScreenDataCopyZ^.detailPen ; blockPen := ScreenDataCopyZ^.blockPen ;
   viewModes := ScreenDataCopyZ^.viewPort.modes ;
   type := ScreenFlagSet{nsExtended} ; defaultTitle := ADR (ProgTitle) ;
  END ; (* WITH *)
 ELSE (* Kick 1.3 Version *)
  Status := IntuiL.GetScreenData (ADR(ScreenDataCopy), SIZE (Screen),
                                  ScreenFlagSet{wbenchScreen}, NIL) ;
  IF Status THEN
   WITH ScreenParm.ns
   DO
    leftEdge := ScreenDataCopy.leftEdge ; topEdge := ScreenDataCopy.topEdge ;
    width := ScreenDataCopy.width ; height := ScreenDataCopy.height ;
    detailPen := ScreenDataCopy.detailPen ; blockPen := ScreenDataCopy.blockPen ;
    viewModes := ScreenDataCopy.viewPort.modes ;
    type := customScreen ;
   END ;
  ELSE
   WITH ScreenParm.ns
   DO
    leftEdge := 0 ; topEdge := 0 ;
    width := 639 ; height := 200 ;
    detailPen := 2 ; blockPen := 1 ;
    viewModes := ViewModeSet{hires} ;
    type := customScreen ;
   END ;
  END ;
  WITH ScreenParm.ns
  DO
   depth := 2 ;
   font := ADR(MyFontParm) ;
   defaultTitle := ADR(ScrTitle) ;
   gadgets := NIL ;
   customBitMap := NIL ;
  END ; (* WITH *)
 END ;
END InitScreenParm ;

PROCEDURE InitWindowParm (VAR WindowParm  : ExtNewWindow;
                          VAR WindowTitle : ARRAY OF CHAR ;
                              ScreenZ     : ScreenPtr ;
                          VAR tagbuf      : tagtype;
                              GadgetZahl  : INTEGER) ;

VAR ScreenDataCopyZ : ScreenPtr ;
    ScreenDataCopy  : Screen ;
    Status          : BOOLEAN ;
    rect            : Rectangle ;
    rectZ           : RectanglePtr ;
    ScreenModeID    : LONGCARD ;

BEGIN
 String.Concat (WindowTitle, TaskAddress) ;
 WITH WindowParm.nw
 DO
  leftEdge := 0 ;
  topEdge := 11 ;
  width := 639 ;
  height := 210 ;
  detailPen := 2 ;
  blockPen := 1 ;
  idcmpFlags := IDCMPFlagSet{closeWindow, rawKey, gadgetUp, newSize, sizeVerify,
                             refreshWindow, newPrefs} ;
  flags := WindowFlagSet{windowSizing, windowDrag, windowDepth, activate,
                         windowClose, rmbTrap, nwExtended, simpleRefresh} ;
  firstGadget := NIL ;
  checkMark := NIL ;
  IF ScreenZ = NIL THEN
   screen := NIL ;
  ELSE
   screen := ScreenZ ;
  END ;
  title := ADR(WindowTitle) ;
  bitMap := NIL ;
  minWidth := 320 ; minHeight := 216 ;
  maxWidth := 639 ; maxHeight := 399 ;
  IF ScreenZ = NIL THEN
   type := ScreenFlagSet{wbenchScreen} ;
  ELSE
   type := customScreen ;
  END ;
 END ;

 IF kickVersion >= 36 THEN
  IF ScreenZ # NIL
  THEN
   ScreenDataCopyZ := ScreenZ
  ELSE
   ScreenDataCopyZ := IntuiL.LockPubScreen(NIL) ;
  END ;
  IF ScreenDataCopyZ # NIL THEN
   WITH WindowParm.nw DO
    topEdge := ScreenDataCopyZ^.barHeight + 1;
    width   := ScreenDataCopyZ^.width - leftEdge ;
    height  := ScreenDataCopyZ^.height - topEdge ;
    detailPen := ScreenDataCopyZ^.detailPen ;
    blockPen := ScreenDataCopyZ^.blockPen ;
   END ;

   ScreenModeID := GraphicsL.GetVPModeID (ADR(ScreenDataCopyZ^.viewPort)) ;
   IF LONGINT(ScreenModeID) # LONGINT(invalidID) THEN
    rectZ := ADR(rect) ;
    IF IntuiL.QueryOverscan (ScreenModeID, rectZ,
                             oScanText) THEN
     WITH WindowParm.nw DO
      leftEdge := ScreenDataCopyZ^.leftEdge ;
      IF leftEdge < 0 THEN leftEdge := 0 ; END ;
      topEdge := ScreenDataCopyZ^.barHeight ;
      width := rectZ^.maxX - rectZ^.minX + 1 ;
      IF leftEdge > 0 THEN width := width - leftEdge ; END ;
      IF width > ScreenDataCopyZ^.width
      THEN width := ScreenDataCopyZ^.width ;
      END ;
      height := rectZ^.maxY - rectZ^.minY + 1 ;
      IF topEdge > 0 THEN height := height - topEdge ; END ;
      IF height > ScreenDataCopyZ^.height
      THEN height := ScreenDataCopyZ^.height ;
      END ;
      height := height - ((height -
                           INTEGER (ScreenDataCopyZ^.wBorTop) -
                           INTEGER (ScreenDataCopyZ^.wBorBottom)) MOD GadgetZahl) ;
      (* damit die Gadgets genau passen *)
     END ;
    END ; (* QueryOverscan *)
   END ; (* ScreenModeID *)
   WITH WindowParm.nw DO
    maxHeight := ScreenDataCopyZ^.height ;
    maxWidth := ScreenDataCopyZ^.width ;
   END ;
   WindowParm.extension := TAG(tagbuf, (*waLeft, WindowParm.nw.leftEdge,
                                       waTop,  WindowParm.nw.topEdge,
                                       waWidth, WindowParm.nw.width,
                                       waHeight, WindowParm.nw.height,*)
                                       waIDCMP, WindowParm.nw.idcmpFlags,
                                       waTitle, WindowParm.nw.title,
                                       waScreenTitle,
    ADR("VOK : Auswahl mit Crsr+Return, Abbruch mit Esc oder Mausbedienung"),
                                       waMinWidth, WindowParm.nw.minWidth,
                                       waMinHeight, WindowParm.nw.minHeight,
                                       waMaxWidth, WindowParm.nw.maxWidth,
                                       waMaxHeight, WindowParm.nw.maxHeight,
                                       waFlags, WindowParm.nw.flags,
                                       waSimpleRefresh, TRUE,
                                       waAutoAdjust, TRUE,
                                       tagEnd) ;
   IF ScreenZ = NIL
   THEN
    IntuiL.UnlockPubScreen (NIL, ScreenDataCopyZ) ;
   END ;
  ELSE (* 1.3 Version *)
   Status := IntuiL.GetScreenData (ADR(ScreenDataCopy), SIZE (Screen),
                                   ScreenFlagSet{wbenchScreen}, NIL) ;
   IF Status THEN
    WITH WindowParm.nw
    DO
     leftEdge := 0 ;
     topEdge := ScreenDataCopy.barHeight + 1 ;
     width := ScreenDataCopy.width ;
     height := ScreenDataCopy.height - topEdge ;
     height := height - ((height -
                          INTEGER (ScreenDataCopy.wBorTop) -
                          INTEGER (ScreenDataCopy.wBorBottom)) MOD GadgetZahl) ;
     (* damit die Gadgets genau passen *)
     maxHeight := ScreenDataCopy.height ;
     maxWidth  := ScreenDataCopy.width ;
     detailPen := ScreenDataCopy.detailPen ;
     blockPen := ScreenDataCopy.blockPen ;
    END ;
   ELSE
    Fehlermeldung (keineScreenDaten) ;
   END ;
  END ;
 END ;
END InitWindowParm ;

PROCEDURE InitIntuiText (VAR ScrZ       : ScreenPtr ;
                         VAR IntuiTextS : IntuiText;
                                   x, y : INTEGER ;
                         VAR Text       : STRING) ;

BEGIN
 WITH IntuiTextS
 DO
  frontPen := 1 ;
  backPen := 2 ;
  drawMode := jam1 ;
  leftEdge := x ;
  topEdge := y ;
  nextText := NIL ;
  iTextFont := ScrZ^.font ;
  iText := ADR(Text) ;
 END ;
END InitIntuiText;

PROCEDURE InitEinstellungsMenue (VAR GMParm : GMPT) ;

BEGIN
 WITH GMParm DO
  xPos   := -1 ;
  yPos   := -1 ;
  width  := 0 ;
  height := 0 ;
  RahmenPos := 1 ;
  toggle := TRUE ;
  Menuepunkt[0] := "Einstellungen sichern" ;
  Menuepunkt[1] := "Eigenen Bildschirm öffnen" ;
  Menuepunkt[2] := "Farbsignal bei der Wortabfrage" ;
  Menuepunkt[3] := "Bildschirmsignal bei falscher Antwort" ;
  Menuepunkt[4] := "Bedeutungsausgabe auch bei richtiger Antwort" ;
  Menuepunkt[5] := "Programm automatisch vom CLI lösen" ;
  Menuepunkt[6] := "Wortdateien mit PowerPacker©® beim speichern packen" ;
  Menuepunkt[7] := "Copyright bei Programmstart anzeigen" ;
  GadgetZ := 7 ;
 END ;
END InitEinstellungsMenue ;

PROCEDURE InitHauptmenue (VAR GMParm : GMPT) ;

BEGIN
 WITH GMParm DO
  xPos   := -1 ;
  yPos   := -1 ;
  width  := 0 ;
  height := 0 ;
  RahmenPos := 1 ;
  toggle := FALSE ;
  Menuepunkt[0] := "Programm beenden" ;
  Menuepunkt[1] := ProgTitle ;
  Menuepunkt[2] := "Neue Wörter eingeben" ;
  Menuepunkt[3] := "Wort löschen" ;
  Menuepunkt[4] := "Wort ändern" ;
  Menuepunkt[5] := "Wort suchen" ;
  Menuepunkt[6] := "Lernerfolg zurücksetzen" ;
  Menuepunkt[7] := "Wortliste ausgeben" ;
  Menuepunkt[8] := "Vokabelspeicher löschen" ;
  Menuepunkt[9] := "Wörter lernen" ;
  Menuepunkt[10] := "Statistik ausgeben" ;
  Menuepunkt[11] := "Speicherinhalt sichern" ;
  Menuepunkt[12] := "Datei dazuladen" ;
  Menuepunkt[13] := "Voreinstellungen" ;
  GadgetZ := 13 ;
 END ;
END InitHauptmenue ;

PROCEDURE DisposeRahmen (BorderZ : BorderPtr) ;

BEGIN
 IF BorderZ # NIL THEN
  IF BorderZ^.nextBorder # NIL THEN
   DisposeRahmen (BorderZ^.nextBorder) ;
  END ;
  Heap.Deallocate (BorderZ^.xy) ;
  Heap.Deallocate (BorderZ) ;
 END ;
END DisposeRahmen ;

PROCEDURE Writevertical (VAR RastPortZ : RastPortPtr ;
                             x, y      : INTEGER;
                                  Text : STRING) ;

(* gibt Text senkrecht aus *)

VAR Laenge, Pos : INTEGER ; Ch : ARRAY [0..1] OF CHAR ;

BEGIN
 Laenge := String.Length (Text) ;
 GraphicsL.SetDrMd (RastPortZ, jam1) ;
 GraphicsL.SetAPen (RastPortZ, 1) ;
 FOR Pos := 0 TO (Laenge - 1) (* "0C" abschneiden *)
 DO
  Ch := "" ;
  String.ConcatChar (Ch,Text[Pos]) ;
  GraphicsL.Move (RastPortZ, x, y) ;
  GraphicsL.Text (RastPortZ, ADR(Ch), 1) ;
  INC (y, RastPortZ^.txHeight) ;
 END ; (* WHILE *)
END Writevertical ;

PROCEDURE ClearWindow (VAR WinZ : WindowPtr) ;

BEGIN
 GraphicsL.SetDrMd (WinZ^.rPort, jam1) ;
 GraphicsL.SetRast (WinZ^.rPort, 0) ;
END ClearWindow;

PROCEDURE About () ;

VAR Text : ARRAY [0..1200] OF CHAR ;

BEGIN
 Text := ProgTitle+"\nJüddeldamm 16, D33397 Rietberg, Germany\n\n"+
                     "EMAIL at the moment: AR.Richter@tu-bs.de\n"+
                     "\n"+
                     "developed with M2Amiga V4.1 (A+L AG, Grenchen)\n"+
                     "ReqTools.library,\nPowerpacker.library copyright Nico François\n"+
                     "ReqToolsSupport - Routines copyright Frank Lömker\n"+
                     "Thanks a lot, folks!\n\n"+
                     "This program is distributed in the hope that it will be useful,\n"+
                     "but without ANY WARRANTY, expressed or implied; even without the\n"+
                     "implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR \n"+
                     "PURPOSE. This program may not be used in healthcritical missions or\n"+
                     "or for direct or indirect support of weapons` production.\n"+
                     "Well, how would you manage to do that anyway? :-)\n"+
                     "It may be redistributed freely as long as the copyright remains\n"+
                     "visible in the program and it is clearly shown if the distributed\n"+
                     "version is somehow different from my original. NO financial profit\n"+
                     "may be gained from this program or its redistribution.\n"+
                     "All this may change in future.\n"+
                     "All rights on this work remain with the author.\n"+
                     "Please write me an EMAIL, postcard etc. if you use this,\n"+
                     "you might be the only one... ;-)\n";
 rts.vEZRequest (ADR (Text), ADR("Accepted"), NIL, ADDRESS(RequesterTags), NIL) ;

 rts.vEZRequest (ADR("Ein Hinweis für alle Benutzer:\n"+
                     "Dieses Programm kann nicht das Lernen \naus Büchern ersetzen, "+
                     "da nur aus dem Kontext\ndie präzise Bedeutung eines Wortes "+
                     "eindeutig\nwird (zum Beispiel in Verbindung mit einem\n"+
                     "Beispielsatz) und aus der bloßen Kenntnis einer deutschen\n"+
                     "Bedeutung sich kein Sprachgefühl entwickeln kann.\n"+
                     "Ebensowenig sind die 'Wertungen' des Lernerfolges wörtlich zu nehmen.\n"+
                     "Ein 'sehr gut' sagt nichts anderes aus, als daß ein Wort\n"+
                     "fünf mal richtig benannt wurde, und das\n"+
                     "heißt natürlich noch nicht, daß ein bestimmtes Wort\n"+
                     "wirklich 'sitzt'.\n"+
                     "Also betrügen Sie sich nicht selber, indem Sie sich auf das\n"+
                     "Programm verlassen und machen Sie nicht womöglich mich\n"+
                     "für evtl. Mißerfolge  verantwortlich.\n"+
                     "Diese langweiligen Belehrungen können in den Voreinstellungen\n"+
                     "abgeschaltet werden."),
                     ADR("Ein Glück!"), NIL,
                     ADDRESS(RequesterTags),NIL) ;
END About ;

PROCEDURE Listeleer (VAR L : Liste) : BOOLEAN ;

BEGIN
 RETURN (L^.Naechster = NIL) ;
END Listeleer ;

PROCEDURE StatWortzahlStr (    Statistik   : StatistikT;
                               Lernerfolg  : Lerntyp;
                           VAR Ausgabe     : STRING;
                               neat        : BOOLEAN) ;

 PROCEDURE Cut (VAR Str : STRING) ;

 VAR Len   : INTEGER ;
     Pos   : LONGINT ;
     Hilfe : STRING ;

 BEGIN
  Len := String.Length (Str) ;
  Pos := String.LastPos (Str, Len, " ") ;
  IF Pos # String.noOccur
  THEN
   IF (Len - Pos) > 0
   THEN
    String.CopyPart (Hilfe, Str, Pos + 1, (Len - Pos)) ;
    Str := Hilfe ;
   END ;
  END ;
 END Cut ;

VAR err   : BOOLEAN ;
    Hilfe : STRING ;
    Zahl  : REAL ;
    Pos   : LONGINT ;
    Len   : INTEGER ;

BEGIN
 ValToStr(Statistik[ORD(Lernerfolg)], FALSE, Hilfe, 10,
                                           10, " ", err) ;
 IF neat
 THEN
  Cut (Hilfe) ;
 END ;
 String.Concat (Ausgabe, Hilfe) ; String.Concat (Ausgabe, " ª (") ;
 IF ((FWZahl + BDZahl) > 0) THEN
  Zahl := ((REAL(Statistik[ORD(Lernerfolg)]) / REAL(FWZahl + BDZahl)) * REAL(100)) ;
  ValToStr(INTEGER(Zahl), FALSE, Hilfe, 10, 4, " ", err) ;
  IF err
  THEN
   Hilfe := "err" ;
  ELSE
   IF neat THEN Cut (Hilfe) ; END ;
  END ;
 ELSE Hilfe := "0" ;
 END ;
 String.Concat (Ausgabe, Hilfe) ; String.Concat (Ausgabe, " %%)") ;
END StatWortzahlStr ;

PROCEDURE Statistikausgeben (Stat : StatistikT) ;

VAR Ausgabe : STRING ;

BEGIN
 Ausgabe := "" ;
 String.Concat(Ausgabe,"Es sind\n") ;
 StatWortzahlStr(Stat, sehrgut, Ausgabe, FALSE) ;
 String.Concat(Ausgabe," sehr gut gelernte,\n") ;
 StatWortzahlStr(Stat, gut, Ausgabe, FALSE) ;
 String.Concat(Ausgabe," gut gelernte,\n") ;
 StatWortzahlStr(Stat, befriedigend, Ausgabe, FALSE) ;
 String.Concat(Ausgabe," befriedigend gelernte,\n") ;
 StatWortzahlStr(Stat, ausreichend, Ausgabe, FALSE) ;
 String.Concat(Ausgabe," ausreichend gelernte,\n") ;
 StatWortzahlStr(Stat, mangelhaft, Ausgabe, FALSE) ;
 String.Concat(Ausgabe," mangelhaft gelernte,\n") ;
 StatWortzahlStr(Stat, ungenuegend, Ausgabe, FALSE) ;
 String.Concat(Ausgabe," ungenuegend gelernte Wörter im Speicher.") ;

 rts.vEZRequest (ADR(Ausgabe), ADR("Weiter"), NIL,
                 ADDRESS(RequesterTags),
                 NIL) ;
END Statistikausgeben ;

PROCEDURE StatistikinString (VAR Ausgabe : STRING;
                             VAR StatZ   : StatZT) ;

BEGIN
 StatWortzahlStr(StatZ^, sehrgut, Ausgabe, TRUE) ;
 String.Concat(Ausgabe,"/ ") ;
 StatWortzahlStr(StatZ^, gut, Ausgabe, TRUE) ;
 String.Concat(Ausgabe,"/ ") ;
 StatWortzahlStr(StatZ^, befriedigend, Ausgabe, TRUE) ;
 String.Concat(Ausgabe,"/ ") ;
 StatWortzahlStr(StatZ^, ausreichend, Ausgabe, TRUE) ;
 String.Concat(Ausgabe,"/ ") ;
 StatWortzahlStr(StatZ^, mangelhaft, Ausgabe, TRUE) ;
 String.Concat(Ausgabe,"/ ") ;
 StatWortzahlStr(StatZ^, ungenuegend, Ausgabe, TRUE) ;
END StatistikinString ;

(*PROCEDURE AnzahlBedeutungen (L : Liste) : LONGCARD ;

(* zählt Zahl der zu L gehörigen Bedeutungen *)

VAR Zahl : CARDINAL ;
    Zeiger : ZeigeraufBDE ;

BEGIN
 Zahl := 0 ;
 IF L # NIL THEN
  IF L^.BDListe # NIL THEN
   Zeiger := L^.BDListe ;
   INC (Zahl) ;
   WHILE Zeiger # NIL DO
    Zeiger := Zeiger^.Naechster ;
    INC (Zahl) ;
   END ;
  END ;
 END ;
 RETURN Zahl ;
END AnzahlBedeutungen ;*)

PROCEDURE Init3dRahmen (VAR WinZ                : WindowPtr ;
                        VAR BorderZ             : BorderPtr ;
                            x1, y1, w, h        : INTEGER;
                            positiv, delete     : BOOLEAN) : BOOLEAN ;

VAR BorderFeld                : Rahmenkoordinaten ;
    BorderFeldZ, BorderFeld2Z : RktZT ;
    err                       : BOOLEAN ;
    ScreenDrawInfoZ           : DrawInfoPtr ;

BEGIN
 IF kickVersion >= 36 THEN
  ScreenDrawInfoZ := IntuiL.GetScreenDrawInfo (WinZ^.wScreen) ;
 END ;
 BorderZ := NIL ;
 IF Heap.Available (FALSE) > (2 * SIZE(Rahmenkoordinaten) + 2 * SIZE(Border)) THEN
  Heap.Allocate (BorderZ, SIZE(Border)) ;
  Heap.Allocate (BorderFeldZ, SIZE(Rahmenkoordinaten)) ;
  Heap.Allocate (BorderFeld2Z, SIZE(Rahmenkoordinaten)) ;
  IF (BorderZ # NIL) AND (BorderFeldZ # NIL) THEN
   Heap.Allocate (BorderZ^.nextBorder, SIZE(Border)) ;
   IF BorderZ^.nextBorder # NIL THEN
    BorderFeld[0] := 0 ; BorderFeld[1] := 0 ;
    BorderFeld[2] := w ; BorderFeld[3] := 0 ;
    BorderFeld[4] := w - 1; BorderFeld[5] := 1;
    BorderFeld[6] := 1 ; BorderFeld[7] := 1 ;
    BorderFeld[8] := 1 ; BorderFeld[9] := h - 1 ;
    BorderFeld[10] := 0 ; BorderFeld[11] := h ;
    BorderFeld[12] := 0 ; BorderFeld[13] := 0 ;
    BorderFeldZ^ := BorderFeld ;
    WITH BorderZ^
    DO
     leftEdge := x1 ;
     topEdge := y1 ;
     IF (kickVersion >= 36)
     THEN
      IF positiv
      THEN
       frontPen := ScreenDrawInfoZ^.pens^[shinePen] ;
      ELSE
       frontPen := ScreenDrawInfoZ^.pens^[shadowPen] ;
      END ;
     ELSIF positiv
     THEN
      frontPen := 1 ;
     ELSE
      frontPen := 2 ;
     END ;
     IF delete
     THEN
      frontPen := 0 ;
     END ;
     backPen := 0 ;
     drawMode:= jam1 ;
     count := 7 ;
     xy := ADDRESS(BorderFeldZ) ;
    END ; (* WITH *)

    BorderFeld[0] := w ; BorderFeld[1] := 0 ;
    BorderFeld[2] := w ; BorderFeld[3] := h ;
    BorderFeld[4] := 0 ; BorderFeld[5] := h;
    BorderFeld[6] := 1 ; BorderFeld[7] := h - 1 ;
    BorderFeld[8] := w - 1; BorderFeld[9] := h - 1;
    BorderFeld[10] := w - 1 ; BorderFeld[11] := 1 ;
    BorderFeld[12] := w ; BorderFeld[13] := 0 ;
    BorderFeld2Z^ := BorderFeld ;

    WITH BorderZ^.nextBorder^
    DO
     leftEdge := x1 ;
     topEdge := y1 ;
     drawMode := jam1 ;
     IF kickVersion >= 36 THEN
      IF positiv
      THEN
       frontPen := ScreenDrawInfoZ^.pens^[shadowPen] ;
      ELSE
       frontPen := ScreenDrawInfoZ^.pens^[shinePen] ;
      END ;
     ELSE
      IF positiv
      THEN
       frontPen := 2 ;
      ELSE
       frontPen := 1 ;
      END ;
     END ;
     IF delete
     THEN
      frontPen := 0 ;
     END ;
     backPen := 0 ;
     count := 7 ;
     xy := ADDRESS(BorderFeld2Z) ;
     nextBorder := NIL ;
    END ;
   ELSE
    err := TRUE ;
   END ;
  ELSE
   err := TRUE ;
  END ;
 ELSE
  err := TRUE ;
 END ;
 IF err THEN
  Fehlermeldung (keinRahmen) ;
 END ;
 IF kickVersion >= 36 THEN
  IntuiL.FreeScreenDrawInfo (WinZ^.wScreen, ScreenDrawInfoZ) ;
 END ;
 RETURN NOT(err) ;
END Init3dRahmen ;

PROCEDURE InitKnob (VAR WinZ       : WindowPtr ;
                    VAR GadgetS    : Gadget;
                        x, y, w, h : INTEGER;
                        Text       : IntuiTextPtr;
                        toggle     : BOOLEAN ;
                        ID         : INTEGER) ;

VAR BorderZ, selectBorderZ : BorderPtr ;
    GadgetPropInfo         : PropInfo ;
    Status                 : BOOLEAN ;

BEGIN
 Status := Init3dRahmen (WinZ, BorderZ, 0, 0, w, h - 1, TRUE, FALSE) ;
 Status := Init3dRahmen (WinZ, selectBorderZ, 0, 0, w, h - 1, FALSE, FALSE) ;

 WITH GadgetS
 DO
  nextGadget := NIL ;
  leftEdge := x ;
  topEdge := y ;
  width := w ;
  height := h ;
  flags := flags + GadgetFlagSet{gadgHImage} ;
  activation := activation + ActivationFlagSet{relVerify} ;
  IF toggle THEN INCL (activation, toggleSelect) ; END ;
  gadgetType := boolGadget ;
  gadgetText := Text ;
  specialInfo := NIL ;
  gadgetRender := BorderZ ;
  selectRender := selectBorderZ ;
  gadgetID := ID ;
  userData := NIL ;
 END ; (* WITH *)
END InitKnob ;

PROCEDURE ScreenZeiger (VAR ScrZ : ScreenPtr) ;

VAR err : BOOLEAN ;

BEGIN
 IF ScrZ = NIL THEN
  IF kickVersion >= 36 THEN
   ScrZ := IntuiL.LockPubScreen (NIL) ;
   IF ScrZ # NIL THEN IntuiL.UnlockPubScreen (NIL, ScrZ) ; END ;
  ELSE
   err  := IntuiL.CloseWorkBench () ;
   ScrZ := IntuiL.OpenWorkBench () ;
   IF NOT(err) THEN
    err := IntuiL.CloseWorkBench () ;
   END ;
  END ;
 END ;
END ScreenZeiger ;

PROCEDURE InitSignal (VAR CopperlistZ : CopperlistT ;
                          ScrZ        : ScreenPtr;
                          type        : BOOLEAN) ;

VAR ViewPortZ                       : ViewPortPtr ;
    I, J, Z                         : CARDINAL ;

BEGIN
(* CopListZ := GfxMacros.CINIT (CopperlistZ[I], ) ;*)
 ScreenZeiger (ScrZ) ;
 ViewPortZ := ADR(ScrZ^.viewPort) ;
(* FOR J := 0 TO 3 DO
  FOR I := 0 TO ScrZ^.height BY 4 DO (* Jede Zeile des Bildschirms bearbeiten *)
   Z := I DIV 4 ;*)
   CopperlistZ[0] := ExecL.AllocMem(SIZE(UCopList), MemReqSet{chip,memClear}) ;
   IF CopperlistZ[0] # NIL
   THEN
    GfxMacros.CWAIT(CopperlistZ[0], I, 0) ;
    IF type
    THEN
     GfxMacros.CMOVE(CopperlistZ[0], ADR(Hardware.custom.color[Farbe]),
      00C0H + 10H) ;
    ELSE
     GfxMacros.CMOVE(CopperlistZ[0], ADR(Hardware.custom.color[Farbe]),
      0C00H + 100H) ;
    END ;
   ELSE
    Fehlermeldung (Speichermangel) ;
   END ;
  GfxMacros.CEND (CopperlistZ[0]) ;
(* END ;
  END ; *)
END InitSignal ;

PROCEDURE SetSignal (VAR Copperlist  : CopperlistT ;
                         ScrZ        : ScreenPtr ;
                         type        : BOOLEAN) ;

VAR ViewPortZ   : ViewPortPtr ;
    Hoehe, Gr,I : CARDINAL ;
    UCopTagsZ   : tagPtrtype ;
    UCopTags    : tagtype ;
    err         : BOOLEAN ;

BEGIN
 ScreenZeiger (ScrZ) ;
 UCopTagsZ := TAG(UCopTags , vtagUserclipSet, NIL, vtagEndCm, NIL, tagEnd) ;
 Gr := SIZE(UCopListPtr) ;
 IF (Prefs.Beep AND NOT (type)) THEN
  IntuiL.DisplayBeep (ScrZ) ;  END ;
 IF Prefs.Copper THEN
  ViewPortZ := ADR(ScrZ^.viewPort) ;
  IF ViewPortZ # NIL THEN
   Hoehe := CARDINAL(ScrZ^.height) ;
   ExecL.Forbid () ;
   ViewPortZ^.uCopIns := Copperlist[0] ;
   ExecL.Permit () ;
(*   err := GraphicsL.VideoControl(ViewPortZ^.colorMap, UCopTags) ;*)
   IntuiL.RethinkDisplay ()

(*  ASSEMBLE (
   MOVE.L  A6,-(A7)
   MOVE.L  A2,-(A7)
   MOVE.L  A0,-(A7)
   MOVE.L  D1,-(A7)
   CLR.L   D1(* Datenregister loeschen auf Langwortbreite (wegen Berechnungen unten)*)
   MOVE.W  Hoehe(A5),D1 (* Datenregister mit Screenhoehe laden *)
   MOVE.L  ExecL(A4),A6
   MOVE.L  IntuiL(A4),A2
   MOVE.L  ViewPortZ(A5),A0     (* ViewPortZ nach A0 *)
J: MOVE.L  Copperlist(A5),A1  (* ADR(Copperlist) nach A1 *)
   MOVE.L  D1,-(A7)        (* D1 retten *)
   ASL.W   #2,D1 (* D1:=D1*4 *)
   ADDA.L  D1,A1  (* Arrayoffset zum Arrayanfang addieren *)
   JSR     -132(A6) (* ExecL.Forbid *)
   MOVE.L  (A1),ViewPort.uCopIns(A0)
   JSR     -138(A6) (* ExecL.Permit *)
   JSR     -390(A2) (* IntuiL.RethinkDisplay *)
   MOVE.L  (A7)+,D1 (* D1 zurückholen *)
   SUBQ.L  #3,D1
   DBF     D1,J     (* D1 dekrementieren *)
   MOVE.L  (A7)+,D1
   MOVE.L  (A7)+,A0
   MOVE.L  (A7)+,A2
   MOVE.L  (A7)+,A6
   END) ;
   *)
(*
   FOR I := ScrZ^.height TO 0 BY -1 DO
    ExecL.Forbid () ;
    ViewPortZ^.uCopIns := CopperlistZ[I] ;
    ExecL.Permit () ;
    err := VideoControl (ViewPortZ^.colorMap, UCopTags) ;
    IntuiL.RethinkDisplay ()
   END ;
*)
  END ;
 END ;
END SetSignal ;

PROCEDURE RemoveSignal (ScrZ : ScreenPtr) ;

VAR CopperlistZ : UCopListPtr ;
    ViewPortZ   : ViewPortPtr ;

BEGIN
 ScreenZeiger (ScrZ) ;
 IF Prefs.Copper THEN
  ViewPortZ := ADR(ScrZ^.viewPort) ;
  IF ViewPortZ # NIL THEN
   ExecL.Forbid () ;
   IF ViewPortZ^.uCopIns # NIL THEN
    ViewPortZ^.uCopIns := NIL ;
   END ;
   ExecL.Permit () ;
  END ;
  IntuiL.RethinkDisplay ;
 END ;
END RemoveSignal ;

PROCEDURE CenterGadText (VAR gadText : IntuiText ;
                             gadWidth, gadHeight : INTEGER;
                             ScreenZ : ScreenPtr) ;

BEGIN
 gadText.leftEdge := ((gadWidth - IntuiL.IntuiTextLength (ADR(gadText))) DIV 2) ;
 IF gadText.iTextFont # NIL
 THEN gadText.topEdge := ((gadHeight - INTEGER(gadText.iTextFont^.ySize)) DIV 2) ;
 ELSE gadText.topEdge := ((gadHeight - INTEGER(ScreenZ^.font^.ySize)) DIV 2) ;
 END ;
END CenterGadText ;

PROCEDURE Sicherheitsabfrage (VorText : STRING) : BOOLEAN ;

VAR Status : LONGINT ;

BEGIN
 String.Concat (VorText, "Wollen Sie das wirklich ?") ;
 Status :=
  rts.EZRequest (ADR(VorText), ADR("_Nein|_Ja"), NIL, ADDRESS(RequesterTags), NIL) ;
 RETURN (Status = 0) ;
END Sicherheitsabfrage ;

PROCEDURE KillGadgets (VAR WinZ : WindowPtr ;
                       VAR GadgetParm : ARRAY OF Gadget ;
                           GadgetZahl : INTEGER) ;

VAR Zaehler, Pos : INTEGER ;

BEGIN
 FOR Zaehler := 0 TO (GadgetZahl)
 DO
  Pos := IntuiL.RemoveGadget (WinZ, ADR(GadgetParm[Zaehler])) ;
  Heap.Deallocate (GadgetParm[Zaehler].gadgetText) ;
  DisposeRahmen (GadgetParm[Zaehler].gadgetRender) ;
  DisposeRahmen (GadgetParm[Zaehler].selectRender) ;
 END ; (* FOR *)
END KillGadgets ;

PROCEDURE MakeText (VAR ScrZ          : ScreenPtr ;
                        width, height : INTEGER ; (* Fenstergroeße *)
                        GadgetZahl    : INTEGER ;
                    VAR GadText       : ARRAY OF IntuiText ;
                    VAR Text          : ARRAY OF STRING) ;

VAR TextWidth, Zaehler, GadHeight, GadWidth,
    vertGadHeight, vertGadWidth, GadX, GadY,
    gzzHeight, gzzWidth                     : INTEGER ;

BEGIN
 ScreenZeiger (ScrZ) ;
 gzzHeight     := height - 1 - ScrZ^.wBorBottom - ScrZ^.wBorTop ;
 gzzWidth      := width  - 1 - ScrZ^.wBorRight  - ScrZ^.wBorLeft ;
 GadHeight     := (gzzHeight DIV (GadgetZahl)) - 2 ;
 vertGadHeight := gzzHeight - (gzzHeight MOD GadgetZahl) - 2 ;
 TextWidth     := GraphicsL.TextLength (ADR(ScrZ^.rastPort), ADR("w"), 1) ;
 vertGadWidth  := TextWidth + (width DIV 20) ;
 GadWidth      := gzzWidth - 6 - vertGadWidth ;
 FOR Zaehler := 1 TO GadgetZahl
 DO
  InitIntuiText (ScrZ, GadText[Zaehler], 1, 1, Text[Zaehler]) ;
  CenterGadText (GadText[Zaehler], GadWidth, GadHeight, ScrZ) ;
 END ;
END MakeText ;

PROCEDURE MakeGadgets (VAR WinZ       : WindowPtr ;
                       VAR GadgetParm : ARRAY OF Gadget ;
                       VAR GadPos     : ARRAY OF INTEGER ;
                       VAR GadText    : ARRAY OF IntuiText ;
                           GadgetZahl : INTEGER ;
                       VAR Menuepunkt : ARRAY OF STRING ;
                           toggle     : BOOLEAN) ;

VAR TextWidth, Zaehler, GadHeight, GadWidth,
    vertGadHeight, vertGadWidth, GadX, GadY,
    gzzHeight, gzzWidth                     : INTEGER ;

BEGIN
 gzzHeight := WinZ^.height - 1 - WinZ^.borderBottom - WinZ^.borderTop ;
 gzzWidth  := WinZ^.width  - 1 - WinZ^.borderRight - WinZ^.borderLeft ;
 GadHeight     := (gzzHeight DIV (GadgetZahl)) - 2 ;
 vertGadHeight := gzzHeight - (gzzHeight MOD GadgetZahl) - 2 ;
 TextWidth     := GraphicsL.TextLength (WinZ^.rPort, ADR("w"), 1) ;
 vertGadWidth  := TextWidth + (WinZ^.width DIV 20) ;
 GadWidth      := gzzWidth - 6 - vertGadWidth ;

 InitKnob (WinZ, GadgetParm[0], 2 + WinZ^.borderLeft, 2 + WinZ^.borderTop,
           vertGadWidth,
           vertGadHeight, NIL, FALSE, 0) ;

 Writevertical (WinZ^.rPort, GadgetParm[0].leftEdge +
                             (vertGadWidth DIV 2) - (TextWidth DIV 2),
                ((vertGadHeight -
                (INTEGER(WinZ^.rPort^.txHeight) *
                (String.Length (Menuepunkt[0]) - 1))) DIV 2)
                 + 4 + WinZ^.borderTop,
                 Menuepunkt[0]) ;

 GadPos[0] := IntuiL.AddGadget (WinZ, ADR(GadgetParm[0]),
                                0) ;

 GadX := 5 + vertGadWidth + WinZ^.borderLeft ;

 FOR Zaehler := 1 TO GadgetZahl
 DO
  GadY := ((Zaehler - 1) * (GadHeight + 2)) + 2 + WinZ^.borderTop ;
  MakeText (WinZ^.wScreen, WinZ^.width, WinZ^.height, GadgetZahl, GadText,
            Menuepunkt) ;
  InitKnob (WinZ, GadgetParm[Zaehler], GadX, GadY,
                                 GadWidth, GadHeight,
                                 ADR(GadText[Zaehler]), toggle,
                                 Zaehler) ;
  GadPos[Zaehler] := IntuiL.AddGadget (WinZ, ADR(GadgetParm[Zaehler]), Zaehler) ;
 END ; (* FOR *)
 IntuiL.RefreshGadgets (ADR(GadgetParm[0]), WinZ, NIL) ;
END MakeGadgets ;

PROCEDURE LongestiTextLength (VAR  Texte : ARRAY OF IntuiText;
                              VAR    Pos : INTEGER) : INTEGER ;

VAR L1, L2, I : INTEGER ;

BEGIN
 L1 := IntuiL.IntuiTextLength (ADR(Texte[Pos])) ;
 FOR I := (Pos + 1) TO HIGH(Texte) DO
  L2 := IntuiL.IntuiTextLength (ADR(Texte[I])) ;
  IF L2 > L1 THEN L1 := L2 ; Pos := I ; END ;
 END ;
 RETURN L1 ;
END LongestiTextLength;

PROCEDURE CreateGadgetMenu (    ScrZ   : ScreenPtr ;
                            VAR GMP    : GMPT             ;
                            VAR Result : MenuT) : INTEGER ;

VAR ID, Zaehler, Laenge, Pos, minWidth, minHeight, changeHeight,
    GadHeight, GadWidth, vertGadHeight, vertGadWidth,
    gzzHeight, gzzWidth, TextWidth      : INTEGER ;
    adr                                 : GadgetPtr ;
    Zahl                                : LONGINT  ;
    Ausgabe, WindowTitle                : STRING ;
    Status, err, Zahlgedrueckt          : BOOLEAN ;
(*  WinZ                                : WindowPtr ;
    ^^^^ - globale Variable, um Schließen bei Programmabsturz zu ermöglichen *)
    WinParm                             : ExtNewWindow ;
    Extension                           : TextExtent ;
    MesZ                                : IntuiMessagePtr ;
    MesCopy, MesCopy2                   : IntuiMessage ;
    RahmenZ, Rahmen2Z                   : BorderPtr ;
    RegionZ, OldRegionZ                 : RegionPtr ;
    ScreenDrawInfoZ                     : DrawInfoPtr ;
    tagbuf                              : tagtype ;

 PROCEDURE CreateWahlRahmen (VAR        WinZ : WindowPtr;
                                          ID : INTEGER;
                             VAR WahlRahmenZ : BorderPtr;
                                      delete : BOOLEAN) ;

 VAR Status : BOOLEAN ;

 BEGIN
  Status := Init3dRahmen (WinZ, WahlRahmenZ,
                          GMP.GadgetParm[ID].leftEdge - 2,
                          GMP.GadgetParm[ID].topEdge  - 2,
                          GMP.GadgetParm[ID].width    + 4,
                          GMP.GadgetParm[ID].height   + 3, FALSE, delete) ;
  IF Status THEN
   IntuiL.DrawBorder (WinZ^.rPort, WahlRahmenZ, 0, 0) ;
   DisposeRahmen (WahlRahmenZ) ;
  END ;
 END CreateWahlRahmen;

 PROCEDURE FillGadget (VAR   WinZ : WindowPtr ;
                       VAR    GMP : GMPT ;
                              ID  : INTEGER ;
                           toggle : BOOLEAN) ;

 VAR color, textcolor, oldcolor : CARDINAL ;

 BEGIN
  textcolor := 1 ;
  IF kickVersion >= 36 THEN
   ScreenDrawInfoZ := IntuiL.GetScreenDrawInfo (WinZ^.wScreen) ;
   IF ScreenDrawInfoZ # NIL THEN
    IF toggle THEN
     color := ScreenDrawInfoZ^.pens^[fillPen] ;
     textcolor := ScreenDrawInfoZ^.pens^[fillTextPen] ;
    ELSE
     color := ScreenDrawInfoZ^.pens^[backGroundPen] ;
     textcolor := ScreenDrawInfoZ^.pens^[textPen] ;
    END ;
   ELSE
    IF toggle THEN
     color := 3 ;
    ELSE
     color := 0 ;
    END ;
   END ;
  ELSE
   IF toggle THEN
    color := 3 ;
   ELSE
    color := 0 ;
   END ;
  END ;
  GraphicsL.SetAPen  (WinZ^.rPort, color) ;
  GraphicsL.SetDrMd  (WinZ^.rPort, DrawModeSet{}) ;
  GraphicsL.RectFill (WinZ^.rPort, GMP.GadgetParm[ID].leftEdge + 2,
                                   GMP.GadgetParm[ID].topEdge  + 2,
                                   GMP.GadgetParm[ID].leftEdge
                                 + GMP.GadgetParm[ID].width    - 2,
                                   GMP.GadgetParm[ID].topEdge
                                 + GMP.GadgetParm[ID].height   - 3) ;
  IF GMP.GadgetParm[ID].gadgetText # NIL THEN
   oldcolor := GMP.GadgetParm[ID].gadgetText^.frontPen ;
   GMP.GadgetParm[ID].gadgetText^.frontPen := textcolor ;
   IntuiL.PrintIText (WinZ^.rPort, GMP.GadgetParm[ID].gadgetText,
                                   GMP.GadgetParm[ID].leftEdge,
                                   GMP.GadgetParm[ID].topEdge) ;
   GMP.GadgetParm[ID].gadgetText^.frontPen := oldcolor ;
  END ;
 END FillGadget ;

 PROCEDURE FillSelection(VAR   WinZ : WindowPtr ;
                         VAR    GMP : GMPT ;
                         VAR Result : MenuT) ;

 BEGIN
  FOR ID := 1 TO GMP.GadgetZ DO
   IF ID IN Result THEN
    FillGadget (WinZ, GMP, ID, TRUE) ;
   END ;
  END ;
 END FillSelection ;

 PROCEDURE Calcchangeheight (VAR WinZ : WindowPtr ;
                             VAR GMP  : GMPT) : INTEGER ;

 VAR gzzHeight : INTEGER ;

 BEGIN
  gzzHeight := WinZ^.height - WinZ^.borderTop - WinZ^.borderBottom ;
  IF (gzzHeight MOD GMP.GadgetZ) # 0
  THEN
   changeHeight := - (gzzHeight MOD GMP.GadgetZ) + 2 ;
   IF (WinZ^.height + changeHeight <= WinZ^.minHeight)
   THEN
    changeHeight := GMP.GadgetZ - (gzzHeight MOD GMP.GadgetZ) + 2 ;
   END ;
   IF (WinZ^.height + changeHeight > WinZ^.maxHeight)
      OR (WinZ^.height + changeHeight + WinZ^.topEdge > WinZ^.wScreen^.height)
   THEN
    changeHeight := 0 ;
   END ;
  ELSE
   changeHeight := 0 ;
  END ;
  RETURN changeHeight ;
 END Calcchangeheight ;

 PROCEDURE CalcandaddRegion (VAR RegionZ : RegionPtr) ;

 VAR Rect : Rectangle ;

 BEGIN
  WITH Rect DO
   minX := WinZ^.borderLeft ;
   maxX := WinZ^.width - 1 - WinZ^.borderRight ;
   minY := WinZ^.borderTop ;
   maxY := WinZ^.height - 1 - WinZ^.borderBottom ;
  END ;

  IF NOT (GraphicsL.OrRectRegion (RegionZ, ADR(Rect))) THEN
   Fehlermeldung (regionFehler) ;
  END ;
 END CalcandaddRegion ;

BEGIN
 Pos := 1 ; (* bei erstem horizontalen Gadget anfangen *)
 WindowTitle := "VOK - Menü - TaskAdresse: " ;

 InitWindowParm (WinParm, WindowTitle, ScrZ, tagbuf, GMP.GadgetZ) ;

 WITH GMP DO
  IF xPos   >= 0 THEN WinParm.nw.leftEdge := xPos ;   END ;
  IF yPos   >= 0 THEN WinParm.nw.topEdge  := yPos ;   END ;
  IF width  >  0 THEN WinParm.nw.width    := width ;  END ;
  IF height >  0 THEN WinParm.nw.height   := height ; END ;
 END ;

 MakeText (ScrZ, WinParm.nw.width, WinParm.nw.height, GMP.GadgetZ, GMP.GadText,
                 GMP.Menuepunkt) ;
 Laenge := LongestiTextLength (GMP.GadText, Pos) ;

 INCL (WinParm.nw.idcmpFlags, rawKey) ; (* Tastaturabfrage aktivieren *)
 WinZ := IntuiL.OpenWindow (WinParm.nw) ;

 IF WinZ#NIL (* Öffnen verlief fehlerfrei *)
 THEN
  ID := GMP.RahmenPos ;
  IF ID < 0 THEN ID := 0 ; END ;
  IF (GMP.toggle = TRUE) THEN
   FOR Zaehler := 1 TO GMP.GadgetZ DO
    IF Zaehler IN Result THEN
     INCL (GMP.GadgetParm[Zaehler].flags, selected) ;
    END ;
   END ;
  END ;

  RegionZ := GraphicsL.NewRegion () ;

  CalcandaddRegion (RegionZ) ;

  OldRegionZ := LayersL.InstallClipRegion (WinZ^.wLayer, RegionZ) ;

  MakeGadgets (WinZ, GMP.GadgetParm, GMP.GadPos,     GMP.GadText,
                     GMP.GadgetZ,    GMP.Menuepunkt, GMP.toggle) ;

  IF GMP.toggle THEN FillSelection (WinZ, GMP, Result) ; END ;

  minWidth := GraphicsL.TextLength (WinZ^.rPort,
                                    ADR("W"), 1) + Laenge + 47 +
                                    INTEGER(WinZ^.borderRight + WinZ^.borderLeft) ;
  minHeight := GMP.GadgetZ
               * (8 + INTEGER(WinZ^.wScreen^.font^.ySize))
               + INTEGER (WinZ^.borderTop + WinZ^.borderBottom) + 2;

  IF kickVersion >= 36 THEN
   GraphicsL.TextExtent (WinZ^.rPort, ADR("W"), 1, Extension) ;
   Laenge :=  INTEGER(Extension.height)
            * String.Length (GMP.Menuepunkt[0]) + 10
            + INTEGER (WinZ^.borderTop + WinZ^.borderBottom) ;
  ELSE
   Laenge :=   INTEGER(WinZ^.rPort^.txHeight)
             * String.Length (GMP.Menuepunkt[0]) + 10
             + INTEGER(WinZ^.borderTop + WinZ^.borderBottom) ;
  END ;
  IF Laenge > minHeight THEN minHeight := Laenge ; END ;

  Status :=
   IntuiL.WindowLimits
            (WinZ, minWidth, minHeight, 0, 0) ;

  changeHeight := Calcchangeheight (WinZ, GMP) ;

  CreateWahlRahmen (WinZ, ID, RahmenZ, FALSE) ;
  Status := FALSE ;

  REPEAT
   IF changeHeight # 0 THEN
    IntuiL.SizeWindow (WinZ, 0, changeHeight) ;
    changeHeight := 0 ;
   END ;
   MesCopy.class := IDCMPFlagSet{} ;
   ExecL.WaitPort (WinZ^.userPort) ; (* auf Nachricht von Intuition warten *)
   MesZ := ExecL.GetMsg (WinZ^.userPort) ;
   MesCopy := MesZ^ ;
   IF (sizeVerify IN MesCopy.class) THEN
    Pos := IntuiL.RemoveGList (WinZ, ADR(GMP.GadgetParm[0]), GMP.GadgetZ+1) ;
    DosL.Delay (5) ;
   END ;
   ExecL.ReplyMsg (MesZ) ;
   IF MesZ#NIL
   THEN
    IF (rawKey IN MesCopy.class)
    THEN
     IF NOT (repeat IN MesCopy.qualifier)
     THEN
      CreateWahlRahmen (WinZ, ID, RahmenZ, TRUE) ;
      Zahlgedrueckt :=  (MesCopy.code > 0) AND
                        (INTEGER(MesCopy.code) <= GMP.GadgetZ) ;
      IF Zahlgedrueckt
      THEN
       ID := MesCopy.code ; (* Zahl wurde gedrückt *)
      END ;
      CASE MesCopy.code OF
      | CrsrUp         : IF (ID > 0) THEN DEC (ID) ; END ;
      | CrsrDown       : IF (ID < GMP.GadgetZ) THEN INC (ID) ; END ;
      | Esc      : IF (lShift IN MesCopy.qualifier) OR (rShift IN MesCopy.qualifier)
                   THEN
                    ID := 0 ;
                   ELSE
                    ID := -1 ;
                   END ;
                   Status := TRUE ;
      ELSE
      END ; (* CASE *)
      IF (MesCopy.code = Return) OR
         ((rCommand IN MesCopy.qualifier) AND Zahlgedrueckt)
      THEN
       Pos := IntuiL.RemoveGList (WinZ, ADR(GMP.GadgetParm[ID]), 1) ;
       IF (selected IN GMP.GadgetParm[ID].flags)
       THEN
        EXCL (GMP.GadgetParm[ID].flags, selected) ;
       ELSE
        INCL (GMP.GadgetParm[ID].flags, selected) ;
       END ;
       Pos := IntuiL.AddGList (WinZ, ADR(GMP.GadgetParm[ID]),
                               ID, 1, NIL) ;
       IntuiL.RefreshGList (ADR(GMP.GadgetParm[ID]), WinZ, NIL, 1) ;
       IF (NOT GMP.toggle)
       THEN
        FillGadget (WinZ, GMP, ID, TRUE) ;
        DosL.Delay (10) ;
        FillGadget (WinZ, GMP, ID, FALSE) ; (* cause flickering *)
        Zahl := IntuiL.RemoveGList (WinZ, ADR(GMP.GadgetParm[ID]), 1) ;
        IF (selected IN GMP.GadgetParm[ID].flags)
        THEN
         EXCL (GMP.GadgetParm[ID].flags, selected) ;
        ELSE
         INCL (GMP.GadgetParm[ID].flags, selected) ;
        END ;
        Zahl := IntuiL.AddGList (WinZ, ADR(GMP.GadgetParm[ID]),
                                 ID, 1, NIL) ;
       END ;
       FillGadget (WinZ, GMP, ID, (selected IN GMP.GadgetParm[ID].flags)) ;
       IF NOT (ID IN Result)
       THEN INCL (Result, ID) ;
       ELSE EXCL (Result, ID) ;
       END ;
       IF (NOT GMP.toggle) OR (ID = 0) THEN Status := TRUE ; END ;
      END ;
      IF ID >= 0 THEN
       CreateWahlRahmen (WinZ, ID, RahmenZ, FALSE) ;
      ELSE
       CreateWahlRahmen (WinZ, 0, RahmenZ, FALSE) ;
      END ;
     END ;
    END ;
    IF (gadgetUp IN MesCopy.class)
    THEN
     adr := GadgetPtr(MesCopy.iAddress) ;
     IF adr # NIL THEN
      ID := adr^.gadgetID ;
     ELSE
      Fehlermeldung (ZeigerFehler) ;
      ID := 0 ;
     END ;
     CASE ID OF
     |   0 : Status := TRUE ;
     ELSE IF NOT(ID IN Result)
          THEN
           INCL (Result,ID) ;
           FillGadget (WinZ, GMP, ID, TRUE) ;
          ELSE
           EXCL (Result,ID) ;
           FillGadget (WinZ, GMP, ID, FALSE) ;
          END ;
          IF NOT GMP.toggle THEN Status := TRUE ; END ;
     END ; (* CASE *)
    END ;
    IF (newSize IN MesCopy.class) OR (newPrefs IN MesCopy.class)
    THEN
     KillGadgets (WinZ, GMP.GadgetParm, GMP.GadgetZ) ;

     changeHeight := Calcchangeheight (WinZ, GMP) ;

     RegionZ := LayersL.InstallClipRegion (WinZ^.wLayer, OldRegionZ) ;

     GraphicsL.ClearRegion (RegionZ) ;

     CalcandaddRegion (RegionZ) ;

     OldRegionZ := LayersL.InstallClipRegion (WinZ^.wLayer, RegionZ) ;

     ClearWindow (WinZ) ;

     MakeGadgets (WinZ, GMP.GadgetParm, GMP.GadPos, GMP.GadText,
                  GMP.GadgetZ, GMP.Menuepunkt, GMP.toggle) ;

     IF GMP.toggle THEN FillSelection (WinZ, GMP, Result) ; END ;

     CreateWahlRahmen (WinZ, ID, RahmenZ, FALSE) ;
    END ;
    IF (refreshWindow IN MesCopy.class) THEN
     IntuiL.BeginRefresh (WinZ) ;
     CreateWahlRahmen (WinZ, ID, RahmenZ, FALSE) ;
     TextWidth := GraphicsL.TextLength (WinZ^.rPort, ADR("w"), 1) ;
     Writevertical (WinZ^.rPort, GMP.GadgetParm[0].leftEdge +
                   (GMP.GadgetParm[0].width DIV 2) - (TextWidth DIV 2),
                   ((GMP.GadgetParm[0].height -
                   (INTEGER(WinZ^.rPort^.txHeight) *
                   (String.Length (GMP.Menuepunkt[0]) - 1))) DIV 2)
                    + 4 + WinZ^.borderTop,
                    GMP.Menuepunkt[0]) ;
     IF GMP.toggle THEN FillSelection (WinZ, GMP, Result) ; END ;
     IntuiL.EndRefresh (WinZ, TRUE) ;
    END ;
   END ;
  UNTIL (closeWindow IN MesCopy.class) OR Status ;

  GMP.RahmenPos := ID ;

  IF closeWindow IN MesCopy.class THEN ID := -1 ; END ;

  IF WinZ # NIL
  THEN
   WITH GMP DO
    xPos := WinZ^.leftEdge ;
    yPos := WinZ^.topEdge ;
    width := WinZ^.width ;
    height := WinZ^.height ;
   END ;
   GraphicsL.DisposeRegion (RegionZ) ;
   KillGadgets (WinZ, GMP.GadgetParm, GMP.GadgetZ) ;
   IntuiL.CloseWindow (WinZ) ;
   WinZ := NIL ;
  END ;
 ELSE
  Fehlermeldung (nixFenster) ;
 END ;
 RETURN ID ;
END CreateGadgetMenu ;

PROCEDURE FRAC(Zahl : REAL) : REAL;

VAR Zahl2 : LONGCARD;

BEGIN
 Zahl2 := TRUNC(Zahl);
 Zahl := Zahl - FLOAT(Zahl2);
 RETURN Zahl;
END FRAC;

PROCEDURE Vgl (String1, String2 : ARRAY OF CHAR) : BOOLEAN;

BEGIN
 RETURN (String.Compare (String1, String2) = 0) ;
END Vgl;

PROCEDURE Recognize (String1, String2 : STRING; Prozent : INTEGER) : BOOLEAN;

CONST maxlen = Maxzahl;
      p0 = 1;    (****  Ersetzen  ****)
      q0 = 1;    (****  Einfügen  ****)
      r0 = 1;    (****  Löschen   ****)
(*
PROCEDURE Levenshtein (wort, muster : STRING ; VAR limit : INTEGER) : BOOLEAN ;

(****  Gewichtete  Levenshtein-Distanz      ****)
(****  mit Berücksichtigung von Wildcards   ****)
(****  (geschwindigkeitsoptimierte Version) ****)
(****  Ergebnis:  WLD, falls WLD <= limit   ****)
(****             maxlen    sonst           ****)
(****  Autor :  Jörg Michael, Hannover      ****)
(****  Datum :  4. Februar 1993             ****)
(****  Converted to M2 by KAR 13.08.94      ****)

PROCEDURE MIN (x,y,z : INTEGER) : INTEGER ;

BEGIN
  IF (x < y) THEN y := x ; END ; (* kleinste Zahl aus (x , y) steht in y *)
  IF (y < z) THEN RETURN(y) END ;
  RETURN(z) ;
END MIN ;

PROCEDURE isnormal (c : CHAR): BOOLEAN ;

BEGIN
 RETURN ((c <> '*') AND (c <> '?')) ;
END isnormal ;

PROCEDURE WLD (VAR wort,muster: STRING; limit: INTEGER) : INTEGER;

VAR c           : CHAR ;
    i,j,lw,lm   : INTEGER ;
    pp,p,q,r    : INTEGER ;
    d1,d2,spmin : INTEGER ;
    d           : ARRAY [0..maxlen] OF INTEGER ;

BEGIN
 lw := String.Length (wort);
 lm := String.Length (muster);
 spmin := 0;

 IF (limit = 0)
 THEN
  (****  Anfangsbuchstaben vergleichen  ****)
  IF     (lm >= 1) AND (lw >= 1) AND (isnormal (muster[1]))
     AND (muster[1] <> wort[1])
  THEN spmin := maxlen
  END ;
 END ;

 IF (limit = 1)
 THEN
  (****  die ersten zwei Buchstaben vergleichen  ****)
  IF     (lm >= 2)
     AND (lw >= 2)
     AND (isnormal (muster[1])) AND (isnormal (muster[2]))
     AND (muster[1] <> wort[1]) AND (muster[1] <> wort[2])
     AND (muster[2] <> wort[1]) AND (muster[2] <> wort[2])
  THEN spmin := maxlen
  END ;
 END ;

 IF (spmin <= limit)
 THEN
  (****  Sternchen zählen  ****)
  j := 0;
  FOR i := 1 TO lm DO
   IF (muster[i] = '*') THEN j := j+1 ; END ;
  END ;

  (****  Wortlängen prüfen  ****)
  i := lm-j -lw;
  IF (i * q0 > limit) OR ((j = 0) AND (i * r0 < -limit))
  THEN spmin := maxlen
  END ;
 END ;

 IF (spmin <= limit) THEN
  (****  Anfangswerte berechnen  ****)
  IF (lm = 0) THEN
   FOR i := 0 TO lw DO  d[i] := i * r0 ; END
  ELSIF (muster[1] = '*') THEN
   FOR i := 0 TO lw DO d[i] := 0 ; END ;
  ELSE
   IF (muster[1] = '?') THEN p := 0
   ELSE p := MIN (p0, p0, r0+q0) ;
        d[0] := q0;
        d[1] := q0;
        d[2] := q0;
        FOR i := 1 TO lw DO
         IF (wort[i] = muster[1])
         THEN
          p := 0;
          d[i] := (i-1) * r0 + p ;
         END ;
         spmin := MIN (d[0], d[1], d[2]) ;
        END ;
   END ;
  END ;
 END ;

 (****  Distanzmatrix durchrechnen  ****)
 j := 1;
 WHILE (j < lm) AND (spmin <= limit) DO
  j := j+1;
  c := muster[j];
  IF (c = '*') OR (c = '?') THEN p := 0 ELSE p := p0; END ;
  IF (c = '*')              THEN q := 0 ELSE q := q0; END ;
  IF (c = '*')              THEN r := 0 ELSE r := r0; END ;

  d2    := d[0];
  d[0]  := d[0] + q;
  spmin := d[0];

  FOR i := 1 TO lw DO
   (****  d[i] := Minimum dreier Zahlen  ****)
   d1 := d2;
   d2 := d[i];
   IF (wort[i] = c)
   THEN pp := 0
   ELSE pp := p;
        d[i] := MIN (d1+pp, d2+q, d[i-1]+r);
        IF (d[i] < spmin) THEN spmin := d[i] ; END ;
   END ;
  END ; (* FOR *)
 END;

 IF (spmin <= limit) AND (d[lw] <= limit)
 THEN RETURN(d[lw])
 ELSE RETURN(maxlen) ;
 END ;
END WLD ;

PROCEDURE WLD (VAR wort,muster: STRING) : INTEGER ;

VAR c         : CHAR ;
    i,j,lw,lm : INTEGER ;
    pp,p,q,r  : INTEGER ;
    d         : ARRAY [0..maxlen] OF ARRAY [0..maxlen] OF INTEGER ;

BEGIN
  lw := String.Length (wort);
  lm := String.Length (muster);
  d[0,0] := 0;

  FOR i := 1 TO lw DO d[i,0] := d[i-1,0] + r0 ; END ;

  FOR j := 1 TO lm DO
   c := muster[j];
   IF (c = '*') OR (c = '?') THEN p := 0 ELSE p := p0;  END ;
   IF (c = '*')              THEN q := 0 ELSE q := q0 ; END ;
   IF (c = '*')              THEN r := 0 ELSE r := r0;  END ;

   d[0,j] := d[0,j-1] + q;

   FOR i := 1 TO lw DO
    (****  d[i,j] := Minimum dreier Zahlen  ****)
    IF (wort[i] = muster[j]) THEN pp := 0 ELSE pp := p ; END ;
    d[i,j] := MIN (d[i-1,j-1] +pp, d[i,j-1] +q, d[i-1,j] +r)
   END ;
  END ;
  RETURN d[lw,lm] ;
END WLD ;

BEGIN
 RETURN (WLD (wort,muster) <= limit) ;
END Levenshtein ;
*)

PROCEDURE Recog     (    String1, String2 : STRING;
                     VAR OrgLaenge1       : LONGINT;
                     VAR Prozent          : INTEGER;
                     VAR Count            : LONGINT) : BOOLEAN ;

VAR NewString1, NewString2 : STRING ;
    Erstesmal, Laenge      : LONGINT ;

BEGIN
 Erstesmal := String.FirstPos (String2, 0, String1[0]) ;
 Laenge := String.Length (String1) ;

 IF (Erstesmal # String.noOccur) AND (Laenge > 0) THEN
  String.CopyPart (NewString1, String1, 1, (Laenge - 1)) ;
  INC (Count) ;
  String.CopyPart (NewString2, String2, Erstesmal,
                  (String.Length (String2) - Erstesmal)) ;
  RETURN Recog (NewString1, NewString2, OrgLaenge1,
                Prozent, Count) ;
 ELSE
  IF (INTEGER((REAL(Count) / REAL(OrgLaenge1)) * REAL(100)) >= Prozent) THEN
   RETURN TRUE ;
  ELSIF (Laenge > 0) THEN
   String.CopyPart (NewString1, String1, 1, (Laenge - 1)) ;
   RETURN Recog (NewString1, String2, OrgLaenge1, Prozent, Count) ;
  ELSE
   RETURN FALSE ;
  END ;
 END ;
END Recog ;

VAR OrgLaenge1 : LONGINT ;
    Count      : LONGINT ;

BEGIN
 OrgLaenge1 := String.Length (String1) ;
 Count := 0 ;
 RETURN Recog (String1, String2, OrgLaenge1, Prozent, Count) ;
(* RETURN Levenshtein (String2, String1, Prozent) ; *)
END Recognize ;

PROCEDURE Listeninitialisieren (VAR LF, LD : Liste) : BOOLEAN ;

BEGIN
 (* einen Dummyeintrag in die Listen *)
 Heap.Allocate (LF, SIZE(Listeneintrag));
 Heap.Allocate (LD, SIZE(Listeneintrag));
 Heap.Allocate (LF^.BDListe, SIZE(BDListeneintrag));
 Heap.Allocate (LD^.BDListe, SIZE(BDListeneintrag));

 IF (LF # NIL) AND (LD # NIL) AND (LF^.BDListe # NIL) AND (LD^.BDListe # NIL)
 THEN

  WITH LF^ DO
   WITH Wortdaten
   DO
    Wort := 'FremdwortDummy';
    Typ := Fremdwort ;
   END ;
   Naechster := NIL;
   Letzter := NIL;
  END; (* von WITH *)

  WITH LD^ DO
   WITH Wortdaten
   DO
    Wort := 'Bedeutungsdummy' ;
    Typ := Bedeutung ;
   END ;
   Naechster := NIL ;
   Letzter := NIL ;
  END;
  (* anderen Dummy als jeweilige Bedeutung zuweisen *)
  LF^.BDListe^.Zeiger := LD;
  LF^.BDListe^.Naechster := NIL;
  LD^.BDListe^.Zeiger := LF;
  LD^.BDListe^.Naechster := NIL;
  FWZahl := 0;
  BDZahl := 0;
  RETURN TRUE ;
 ELSE
  Fehlermeldung (Speichermangel) ;
  RETURN FALSE ;
 END ;
END Listeninitialisieren;

PROCEDURE WortHochstufen (VAR Wort : ZeigeraufEintrag) ;

BEGIN
 DEC (StatZ^[ORD(Wort^.Lernerfolg)]) ;
 IF (Wort^.Lernerfolg < MaxLerntyp) THEN INC (Wort^.Lernerfolg) ; END ;
 INC (StatZ^[ORD(Wort^.Lernerfolg)]) ;
END WortHochstufen ;

PROCEDURE WortAbstufen (VAR Wort : ZeigeraufEintrag) ;

BEGIN
 DEC (StatZ^[ORD(Wort^.Lernerfolg)]) ;
 Wort^.Lernerfolg := MinLerntyp ;
 INC (StatZ^[ORD(Wort^.Lernerfolg)]) ;
END WortAbstufen ;

PROCEDURE WortNeueinstufen (VAR Wort : ZeigeraufEintrag) ;

BEGIN
 Wort^.Lernerfolg := MinLerntyp ;
 INC (StatZ^[ORD(MinLerntyp)]) ;
END WortNeueinstufen ;

PROCEDURE WortausStatloeschen (VAR Wort : ZeigeraufEintrag) ;

BEGIN
 DEC(StatZ^[ORD(Wort^.Lernerfolg)]) ;
END WortausStatloeschen ;

PROCEDURE Worthinzufuegen (VAR L : Liste; Wort : STRING) :
                               ZeigeraufEintrag;

VAR FreiZeiger : ZeigeraufEintrag;
    Naechster  : ZeigeraufEintrag;
    davor      : ZeigeraufEintrag;
    Typ        : Unterscheidungstyp ;

BEGIN
 Typ := L^.Wortdaten.Typ ;
 Naechster := L^.Naechster;
 davor := L;
 WHILE (Naechster # NIL) AND
       (String.Compare(Wort,
        Naechster^.Wortdaten.Wort) > 0)
 DO
  davor := Naechster;
  Naechster := Naechster^.Naechster;
 END; (* von WHILE *)

 IF Naechster # NIL THEN

  IF String.Compare(Wort,Naechster^.Wortdaten.Wort) < 0 THEN
   IF Heap.Available (FALSE) > (SIZE(Listeneintrag)+SIZE(BDListeneintrag))
                                                   (* Speicher für mindestens *)
   THEN                                            (* einen Bedeutungseintrag *)
    Heap.Allocate (FreiZeiger, SIZE(Listeneintrag));
    IF FreiZeiger # NIL THEN
     IF Typ = Fremdwort THEN INC(FWZahl);
                        ELSE INC(BDZahl);
     END ; (* IF *)
     FreiZeiger^.Wortdaten.Wort := Wort;
     FreiZeiger^.Wortdaten.Typ := Typ ;
     FreiZeiger^.BDListe := NIL;
     WortNeueinstufen(FreiZeiger) ;
     davor^.Naechster := FreiZeiger ;
     Naechster^.Letzter := FreiZeiger ;
     FreiZeiger^.Naechster := Naechster;
     FreiZeiger^.Letzter := davor;
    END ;
   ELSE FreiZeiger := NIL ;
   END ; (* ELSE : FreiZeiger = NIL , Worthinzufuegen fehlgeschlagen *)
  ELSE FreiZeiger := Naechster; (* Wort steht bereits in der Liste ! *)
       WortAbstufen(FreiZeiger) ;
  END; (* von IF *)

 ELSE

  IF Heap.Available (FALSE) > (SIZE(Listeneintrag)+SIZE(BDListeneintrag))
                                                 (* Speicher für mindestens *)
  THEN                                           (* einen Bedeutungseintrag *)
   Heap.Allocate (FreiZeiger, SIZE(Listeneintrag));
   IF FreiZeiger # NIL THEN
    IF Typ = Fremdwort THEN INC(FWZahl);
                       ELSE INC(BDZahl);
    END ; (* IF *)
    FreiZeiger^.Wortdaten.Wort := Wort;
    FreiZeiger^.Wortdaten.Typ := Typ ;
    FreiZeiger^.BDListe := NIL;
    WortNeueinstufen(FreiZeiger) ;
    davor^.Naechster := FreiZeiger;
    FreiZeiger^.Naechster := Naechster;
    FreiZeiger^.Letzter := davor;
   END; (* von IF *)
  ELSE
   FreiZeiger := NIL ;
  END ;

 END ; (* von ELSE *)
 RETURN FreiZeiger;
END Worthinzufuegen;

PROCEDURE BDinBDListe (BDListe : ZeigeraufBDE; Bedeutung : STRING) : BOOLEAN;

(* Diese Prozedur überprüft, ob eine Bedeutung bereits in der Bedeutungsliste *)
(* vorhanden ist *)
(* BDListe : Zeiger auf den Anfang der Bedeutungslist eines Wortes *)
(* Bedeutung : zu überprüfende Bedeutung *)

BEGIN
 IF BDListe # NIL THEN
  WHILE (BDListe # NIL) AND
        (NOT Vgl (BDListe^.Zeiger^.Wortdaten.Wort, Bedeutung)) DO
   BDListe := BDListe^.Naechster;
  END; (* von WHILE *)
  RETURN (BDListe # NIL) ;
 ELSE RETURN FALSE;
 END;
END BDinBDListe;

PROCEDURE BDinBDListeI (BDListe : ZeigeraufBDE;
                        Bedeutung : STRING) : ZeigeraufBDE;

(* Diese Prozedur überprüft, ob eine Bedeutung bereits in der Bedeutungsliste *)
(* vorhanden ist *)
(* BDListe : Zeiger in die Bedeutungsliste eines Wortes *)
(* Bedeutung : zu überprüfende Bedeutung *)
(* BDListe kann sowohl auf den Anfang als auch irgendwo in die BDListe zeigen !*)

BEGIN
 IF BDListe # NIL THEN
  WHILE (BDListe # NIL) AND (NOT
   Recognize (BDListe^.Zeiger^.Wortdaten.Wort, Bedeutung, Prefs.KIProzent)) DO
    BDListe := BDListe^.Naechster;
  END; (* von WHILE *)
  RETURN BDListe ;
 ELSE RETURN NIL ;
 END;
END BDinBDListeI;

PROCEDURE BDListenEintraghinzufuegen (VAR QuellZeiger : ZeigeraufEintrag;
                                      VAR BDZeiger : ZeigeraufEintrag) : BOOLEAN ;

(* Diese Prozedur fügt einer Bedeutungsliste eine Bedeutung hinzu *)
(* QuellZeiger : Zeiger auf Worteintrag, zu dessen Bedeutungsliste
                 "BDZeiger" hinzugefügt werden soll *)
(* BDZeiger : Zeiger auf hinzuzufügenden Wortlisteneintrag *)
(* Rueckgabewert : Konnte genug Speicher alloziert werden ? *)

VAR Zeiger : ZeigeraufBDE;

BEGIN
IF NOT (BDinBDListe(QuellZeiger^.BDListe, BDZeiger^.Wortdaten.Wort)) THEN
 Zeiger := QuellZeiger^.BDListe;
 IF Zeiger # NIL THEN
  WHILE (Zeiger^.Naechster # NIL) DO
   Zeiger := Zeiger^.Naechster;
  END; (* von WHILE *)
  Heap.Allocate (Zeiger^.Naechster, SIZE(BDListeneintrag));
  IF Zeiger^.Naechster # NIL
  THEN
   Zeiger^.Naechster^.Naechster := NIL;
   Zeiger^.Naechster^.Zeiger := BDZeiger;
   RETURN TRUE ;
  ELSE
   RETURN FALSE ;
  END ; (* ELSE *)
 ELSE
  Heap.Allocate (QuellZeiger^.BDListe, SIZE(BDListeneintrag));
  IF QuellZeiger^.BDListe # NIL
  THEN
   QuellZeiger^.BDListe^.Naechster := NIL;
   QuellZeiger^.BDListe^.Zeiger := BDZeiger;
   RETURN TRUE ;
  ELSE
   RETURN FALSE ;
  END ;
 END; (* ELSE *)
ELSE RETURN TRUE ; (* Bedeutung steht bereits in der Bedeutungsliste ! *)
END; (* von IF *)
END BDListenEintraghinzufuegen;

PROCEDURE Bedeutungzuordnen (VAR L : Liste;
                             VAR Eintrag : ZeigeraufEintrag;
                             Bedeutung : STRING) : BOOLEAN;

(* Diese Prozedur ordnet die Bedeutung des mit "Eintrag" repräsentierten Wortes
   in ihre Hauptliste ein *)

VAR Zeiger, BDZeiger : ZeigeraufEintrag ;

BEGIN
 (* Zuerst nach Bedeutung suchen *)
 Zeiger := L^.Naechster;
 WHILE (Zeiger # NIL) AND (NOT
       Vgl (Zeiger^.Wortdaten.Wort,Bedeutung))
 DO
  Zeiger := Zeiger^.Naechster;
 END; (* von WHILE *)
 IF Zeiger # NIL
 THEN BDZeiger := Zeiger;
 ELSE BDZeiger := Worthinzufuegen (L, Bedeutung);
 END; (* von ELSE *)
 IF BDZeiger # NIL (* Worthinzufuegen ist nicht schiefgegangen oder *)
                   (* Wort stand schon in der Liste *)
 THEN
  (* Nun Zeigerlisten anpassen *)
  RETURN (BDListenEintraghinzufuegen (BDZeiger, Eintrag)
         AND
          BDListenEintraghinzufuegen (Eintrag, BDZeiger))
 ELSE
  RETURN FALSE ;
 END ;
END Bedeutungzuordnen;

PROCEDURE LernerfolgToStr (Lernerfolg : Lerntyp; VAR Ergebnis : STRING) ;

(* Diese Prozedur konvertiert den Inhalt der Variable "Lernerfolg" *)
(* in das ausgeschriebene STRING - Äquivalent *)

BEGIN
 CASE Lernerfolg OF
 | sehrgut : String.Concat(Ergebnis, "sehr gut") ;
 | gut : String.Concat (Ergebnis, "gut") ;
 | befriedigend : String.Concat (Ergebnis, "befriedigend") ;
 | ausreichend : String.Concat (Ergebnis, "ausreichend") ;
 | mangelhaft : String.Concat (Ergebnis, "mangelhaft") ;
 | ungenuegend : String.Concat (Ergebnis, "ungenügend") ;
 END ; (* CASE *)
END LernerfolgToStr ;

PROCEDURE Lernerfolgausgeben (WinZ : WindowPtr; Lernerfolg : Lerntyp);

VAR Ausgabe : STRING ;

BEGIN
 LernerfolgToStr (Lernerfolg, Ausgabe) ;
 Windows.WriteS (WinZ, Ausgabe) ;
END Lernerfolgausgeben;

PROCEDURE Listewaehlen (Title : ARRAY OF CHAR) : BOOLEAN ;

(* TRUE : Fremdwortliste *)
(* FALSE : Deutscheliste *)

VAR Antwort : LONGINT ;

BEGIN
 Antwort := rts.EZRequest (ADR(Title),
                           ADR("_Fremdwortliste|_Bedeutungsliste"), NIL,
                           ADDRESS(RequesterTags), NIL) ;
 RETURN (Antwort = 1) ;
END Listewaehlen ;

PROCEDURE Lernerfolgabfragen() : Lerntyp;

VAR tagbuf : tagtype ;

BEGIN
 CASE rts.EZRequest (ADR("Bitte geben Sie an, bis zu welcher Lernstufe\n"+
                     "Sie die Vokabeln lernen möchten !\n"),
                     ADR("_sehr gut|_gut|_befriedigend|_ausreichend|_mangelhaft"),
                     NIL, ADDRESS (RequesterTags), NIL)
 OF
 |  1 : RETURN (sehrgut) ;
 |  2 : RETURN (gut) ;
 |  3 : RETURN (befriedigend) ;
 |  4 : RETURN (ausreichend) ;
 |  0 : RETURN (mangelhaft) ;
 ELSE ;
 END ; (* CASE *)
END Lernerfolgabfragen;

PROCEDURE WortundAequivReqausgeben (VAR Zeiger : ZeigeraufEintrag;
                                        VorText : STRING);

(* Diese Prozedur gibt ein Wort und seine Bedeutungen aus *)
(* Zeiger : Zeiger auf das Wort, das ausgegeben werden soll *)

VAR BDZeiger : ZeigeraufBDE ;
    Ausgabe  : STRING ;

BEGIN
 BDZeiger := Zeiger^.BDListe ;
 Ausgabe := "" ;
 String.Concat (Ausgabe, VorText) ;
 String.Concat (Ausgabe, Zeiger^.Wortdaten.Wort);
 String.Concat (Ausgabe, "\n\n bedeutet\n") ;
 WHILE BDZeiger # NIL DO
  String.Concat (Ausgabe, "\n") ;
  String.Concat (Ausgabe, BDZeiger^.Zeiger^.Wortdaten.Wort);
  String.Concat (Ausgabe, ",") ;
  BDZeiger := BDZeiger^.Naechster;
 END;
 String.Concat (Ausgabe, "\n\nLernerfolg: ");
 LernerfolgToStr(Zeiger^.Lernerfolg, Ausgabe);
 rts.vEZRequest (ADR(Ausgabe), ADR("Weiter"), NIL, ADDRESS(RequesterTags), NIL) ;
END WortundAequivReqausgeben;

PROCEDURE WortundAequivausgeben (WinZ : WindowPtr;
                                 Zeiger : ZeigeraufEintrag;
                                 Breite, Laenge : CARDINAL);

(* ACHTUNG !!! Verwendet globale Variablen Color0, Color1 *)

(* Diese Prozedur gibt ein Wort und seine Bedeutungen aus *)
(* WinZ : Zeiger auf das Fenster, in die die Ausgabe erfolgt *)
(* Zeiger : Zeiger auf das Wort, das ausgegeben werden soll *)
(* Breite : Fensterbreite in Zeichen *)
(* Laenge : FensterLaenge in Zeichen *)

 PROCEDURE MakePointBar (VAR Bar : ARRAY OF CHAR; Length : CARDINAL) ;

 VAR Zaehler : CARDINAL ;

 BEGIN
  FOR Zaehler := 1 TO Length DO
   String.Concat (Bar, "·") ;
  END ;
 END MakePointBar ;

VAR BDZeiger : ZeigeraufBDE;
    l, c : INTEGER ;
    PointBar : ARRAY [0..80] OF CHAR ;
    Color2 : INTEGER ;

BEGIN
 IF WinZ # NIL THEN
  BDZeiger := Zeiger^.BDListe;
  Windows.SetColor (WinZ, Color0, 0) ;
  Windows.WriteS (WinZ, Zeiger^.Wortdaten.Wort);

  Color2 := Color0 ; Color0 := Color1 ; Color1 := Color2 ;

  WHILE BDZeiger # NIL DO
   Windows.ClearEOL (WinZ) ;
   Windows.WriteS (WinZ, ', ') ;
   Windows.GetPos (WinZ, l, c) ;

   IF CARDINAL (c + String.Length (BDZeiger^.Zeiger^.Wortdaten.Wort) + 26)
      > Breite
   THEN Windows.WriteL (WinZ) ;
   END ;

   Windows.GetPos (WinZ, l, c) ;
   IF CARDINAL(l) < Laenge THEN
    Windows.WriteS(WinZ, BDZeiger^.Zeiger^.Wortdaten.Wort);
    Windows.ClearEOL (WinZ) ;
   END ;
   BDZeiger := BDZeiger^.Naechster;
  END;
  Windows.GetPos (WinZ, l, c) ;
  IF CARDINAL(l) < Laenge
  THEN
   IF Breite > 27 + CARDINAL (c)
   THEN
    MakePointBar (PointBar, Breite - 27 - CARDINAL(c)) ;
    Windows.WriteS (WinZ, ' ') ;
    Windows.WriteS (WinZ, PointBar) ;
   END ;
   Windows.WriteS (WinZ, ' Lernerfolg: ');
   Lernerfolgausgeben (WinZ, Zeiger^.Lernerfolg);
   Windows.WriteL (WinZ) ;
  END ;
 END ;
END WortundAequivausgeben;

PROCEDURE Wortnummerxholen (L          : Liste;
                            Nummer     : LONGCARD;
                            Lernerfolg : Lerntyp) : ZeigeraufEintrag;

(* Diese Prozedur zaehlt alle Wörter mit schlechterem Lernerfolg als *)
(* angegeben durch, bis die gewünschte Nummer erreicht ist. *)
(* Dann gibt sie den Zeiger auf das Wort mit der gewünschten Nummer zurück, *)
(* oder NIL, falls alle Wörter bereits einen besseren Lernerfolg als angegeben *)
(* haben. *)

VAR Zeiger : ZeigeraufEintrag;
    Zaehler : LONGCARD;

BEGIN
 Zaehler := 0;
 Zeiger := L;
 IF (Zeiger^.Naechster = NIL) THEN RETURN NIL ; END ;
 REPEAT
  Zeiger := Zeiger^.Naechster;
  IF (Zeiger = NIL)
  THEN
   IF (Zaehler = 0)
   THEN RETURN NIL ;
   ELSE
    Zeiger := L^.Naechster ;
   END ;
  END ;
  IF (Zeiger^.Lernerfolg < Lernerfolg)
  THEN
   INC (Zaehler) ;
  END ;
 UNTIL (Zaehler >= Nummer);
 RETURN Zeiger ;
END Wortnummerxholen;

PROCEDURE Wortlisteausgeben (L : Liste; ScreenZ : ScreenPtr) ;

CONST Leiste = 65535 ;

VAR Zeiger                       : ZeigeraufEintrag ;
(*    WinZ                         : WindowPtr ; ««« -
       globale Variable, um Schließen bei Programabsturz zu ermöglichen *)
    WindowParm                   : ExtNewWindow ;
    ScrollGadgetZ                : GadgetPtr ;
    WinTitle                     : STRING ;
    MesZ                         : IntuiMessagePtr ;
    MesCopy                      : IntuiMessage ;
    Ausgabe                      : STRING ;
    Status                       : (Refresh, NoRefresh) ;
    ListenTyp                    : Unterscheidungstyp ;
    err                          : BOOLEAN ;
    adr                          : GadgetPtr ;
    l, c, x, y                   : INTEGER ;
    erstesWortNummer, WortZahl, vertPot, vertBody, ZeilenZahl, cHeight,
    letzteWortNummer             : LONGCARD ;
    ratio                        : LONGREAL ;
    PropInfoFlagS                : PropInfoFlagSet ;
    tagbuf                       : tagtype ;

BEGIN
 ListenTyp := L^.Wortdaten.Typ ;

 IF ListenTyp = Fremdwort
 THEN
  WortZahl := FWZahl ;
  ValToStr (FWZahl, FALSE, Ausgabe, 10, 10, "-", err) ;
  WinTitle := "Fremdwortliste, Anzahl der Einträge: " ;
 ELSE
  WortZahl := BDZahl ;
  ValToStr (BDZahl, FALSE, Ausgabe, 10, 10, "-", err) ;
  WinTitle := "Bedeutungsliste, Anzahl der Einträge: " ;
 END ;
 String.Concat (WinTitle, Ausgabe) ;
 String.Concat (WinTitle, " Taskadresse: ") ;

 InitWindowParm (WindowParm, WinTitle, ScreenZ, tagbuf, 13) ;

 Windows.OpenWindow (WinZ, WindowParm.nw.leftEdge, WindowParm.nw.topEdge,
                           WindowParm.nw.width,    WindowParm.nw.height,
                     WinTitle,
                     Windows.WinGadSet{sizing, moving, arranging, closing,
                                       scrolling}) ;
 IF WinZ # NIL THEN
  IntuiL.SetWindowTitles (WinZ, ADR(WinTitle),
         ADR("Rollen mit Crsr, Abbruch mit Esc oder Mausbedienung")) ;
  err := IntuiL.WindowLimits (WinZ, WindowParm.nw.width, 0, 0, 0) ;
  WinZ^.flags :=
   WinZ^.flags + WindowFlagSet{rmbTrap, activate, windowActive} ;
  IntuiL.ModifyIDCMP (WinZ, IDCMPFlagSet{closeWindow,
                                         rawKey, newSize, gadgetUp}) ;

  ScrollGadgetZ := WinZ^.firstGadget ;

  WHILE ScrollGadgetZ^.gadgetType # propGadget DO
   ScrollGadgetZ := ScrollGadgetZ^.nextGadget ;
  END ;

  PropInfoFlagS := PropInfoFlagSet{autoKnob, freeVert} ;
  IF kickVersion >= 36 THEN
   INCL (PropInfoFlagS, propNewLook) ;
  END ;

  IntuiL.RefreshWindowFrame (WinZ) ;
  IntuiL.ActivateWindow (WinZ) ;
  erstesWortNummer := 1 ;
  vertPot := 0 ;
  Status := Refresh ;
  REPEAT
   Windows.SetPos (WinZ, 0, 0) ;
   Windows.GetSize (WinZ, l, c) ;

   IF (WortZahl > CARDINAL(l)) AND (l >= 2)
   THEN
    ZeilenZahl := ( l - 2 ) ;
    ratio := LONGREAL (ZeilenZahl) / LONGREAL(WortZahl) ;
    vertPot := LONGCARD(TRUNC(REAL (erstesWortNummer - 1) * (REAL (Leiste) /
                              REAL (WortZahl + 1 - CARDINAL (l))))) ;
   ELSE
    ratio := LONGREAL (1) ;
    vertPot := LONGCARD(0) ;
   END ;

   cHeight := PropInfoPtr(ScrollGadgetZ^.specialInfo)^.cHeight ;
   vertBody := CARDINAL(TRUNC(ratio * LONGREAL(Leiste))) ;

   IntuiL.NewModifyProp (ScrollGadgetZ, WinZ, NIL, PropInfoFlagS, 0, vertPot, 9,
                      vertBody, 1);
   Windows.GetPos (WinZ, y, x) ;
   Zeiger := Wortnummerxholen (L, erstesWortNummer, MaxLerntyp) ;
   Windows.GetSize (WinZ, l, c) ;
   IF Status = Refresh
   THEN
    Windows.Clear (WinZ) ;
    WHILE (Zeiger # NIL) AND (y < (l - 1)) DO
     WortundAequivausgeben (WinZ, Zeiger, c, l);
     Windows.GetPos (WinZ, y, x) ;
     Zeiger := Zeiger^.Naechster;
     Windows.GetSize (WinZ, l, c) ;
    END ;
   END ;

   REPEAT
    MesCopy.class := IDCMPFlagSet{} ;
    ExecL.WaitPort (WinZ^.userPort) ; (* auf Nachricht von Intuition warten *)
    MesZ := ExecL.GetMsg (WinZ^.userPort) ;
    MesCopy := MesZ^ ;
    ExecL.ReplyMsg (MesZ) ;
    IF MesZ#NIL
    THEN
     IF (gadgetUp IN MesCopy.class)
     THEN
      ScrollGadgetZ := GadgetPtr(MesCopy.iAddress) ;
      IF ScrollGadgetZ # NIL THEN
       IF ScrollGadgetZ^.gadgetType = propGadget THEN

        (* Es war das Proportionalgadget *)
        IF ScrollGadgetZ^.specialInfo # NIL
        THEN
         vertPot := PropInfoPtr(ScrollGadgetZ^.specialInfo)^.vertPot ;
         cHeight := PropInfoPtr(ScrollGadgetZ^.specialInfo)^.cHeight ;
         IF WortZahl > CARDINAL(l)
         THEN
          erstesWortNummer := CARDINAL(TRUNC(REAL(vertPot) *
                                       REAL(WortZahl + 1 - CARDINAL (l)) /
                                       REAL(Leiste))) + 1 ;
         ELSE
          erstesWortNummer := 1 ;
         END ;
         Status := Refresh ;
        ELSE Fehlermeldung (ZeigerFehler) ;
        END ;
       END ;
      END ;
     END ;
     IF (newSize IN MesCopy.class)
     THEN
      erstesWortNummer := 1 ;
      Status := Refresh ;
     END ;
     IF (rawKey IN MesCopy.class)
     THEN
      CASE MesCopy.code OF
      | CrsrUp :
         IF erstesWortNummer > 1
         THEN
          DEC (erstesWortNummer) ;
          Windows.Scroll (WinZ, -1) ;
          Windows.SetPos (WinZ, 0, 0) ;
          Zeiger := Wortnummerxholen (L, erstesWortNummer, MaxLerntyp) ;
          WortundAequivausgeben (WinZ, Zeiger, c, l) ;
          Status := NoRefresh ;
         END ;
      | CrsrDown :
       IF erstesWortNummer + CARDINAL(l) <= (WortZahl + 1)
       THEN
        INC (erstesWortNummer) ;
        Windows.Scroll (WinZ, 1) ;
        Windows.SetPos (WinZ, l - 2, 0) ;
        Zeiger := Wortnummerxholen (L, erstesWortNummer + LONGCARD(l) - 2, MaxLerntyp) ;
        WortundAequivausgeben (WinZ, Zeiger, c, l) ;
        Windows.ClearEOL (WinZ) ;
        Status := NoRefresh ;
       END ;
      | Esc :
         INCL (MesCopy.class, closeWindow) ;
         Status := NoRefresh ;
      ELSE
       Status := NoRefresh ;
      END ;
     END ;
    END ; (* IF *)
   UNTIL    (closeWindow IN MesCopy.class) OR (Status = Refresh)
         OR (rawKey IN MesCopy.class) ;
   IF (newSize IN MesCopy.class) THEN Windows.Clear (WinZ) ; END ;
  UNTIL (closeWindow IN MesCopy.class) ;
  Windows.CloseWindow (WinZ) ;
  WinZ := NIL ;
 ELSE Fehlermeldung (nixFenster) ;
 END ;
END Wortlisteausgeben;

PROCEDURE Listewaehlenundausgeben (    LD, LF : Liste;
                                       ScreenZ : ScreenPtr) ;

BEGIN
 IF Listewaehlen ("Welche Liste ausgeben ?") THEN
  Wortlisteausgeben (LD, ScreenZ) ;
 ELSE
  Wortlisteausgeben (LF, ScreenZ) ;
 END ;
END Listewaehlenundausgeben ;

(*PROCEDURE LetzterEintrag (L:Liste) : ZeigeraufEintrag ;

BEGIN
 IF L^.Naechster # NIL
 THEN
  RETURN LetzterEintrag (L^.Naechster) ;
 ELSE
  RETURN L ;
 END ;
END LetzterEintrag;*)

PROCEDURE FindeWort (L : Liste; Suchwort : STRING) : ZeigeraufEintrag;

VAR Zeiger : ZeigeraufEintrag ;

BEGIN
 Zeiger := L^.Naechster ;
 IF Suchwort[0] # ' '
 THEN
  WHILE Zeiger # NIL
  DO
   IF Vgl (Suchwort,Zeiger^.Wortdaten.Wort)
   THEN
    RETURN Zeiger;
   END ;
   Zeiger := Zeiger^.Naechster ;
  END ; (* WHILE *)
  RETURN Zeiger ;
 END ; (* IF *)
 RETURN NIL ;
END FindeWort;

PROCEDURE FindeWortI (       L : Liste;
                      Suchwort : STRING;
                       Prozent : LONGINT) : ZeigeraufEintrag;

VAR Zeiger : ZeigeraufEintrag ;

BEGIN
 Zeiger := L^.Naechster ;
 IF Suchwort[0] # ' '
 THEN
  WHILE Zeiger # NIL
  DO
   IF Recognize (Suchwort,Zeiger^.Wortdaten.Wort, Prozent)
   THEN
    RETURN Zeiger;
   END ;
   Zeiger := Zeiger^.Naechster ;
  END ; (* WHILE *)
  RETURN Zeiger ;
 END ; (* IF *)
 RETURN NIL ;
END FindeWortI;

PROCEDURE Wortsuchen (L1, L2 : Liste; ScreenZ : ScreenPtr) ;

VAR Suchwort      : STRING ;
    Wahl, Prozent : LONGINT ;
    gefunden      : BOOLEAN ;
    tagbuf        : tagtype ;
    HilfsZ        : ADDRESS ;

BEGIN
 HilfsZ := TAG (tagbuf, rt.Screen, ScreenZ, rt.ReqPos,
                rt.ReqPosCenterScr, rt.gsWidth, 600,
                rt.glTextFmt,
                ADR("Bis zu welchem Prozentsatz\nan Übereinstimmnung\nsoll das Wort erkannt werden ?"),
                tagEnd) ;
 Wahl := rts.EZRequest (
  ADR("Soll in der Fremdwortliste oder in der deutschen Liste gesucht werden."),
  ADR("_Fremdwortliste|_Deutsche Liste"), NIL, ADDRESS(RequesterTags), NIL) ;

 IF (Wahl = 0) THEN L1 := L2 ; END ;

 Wahl := 1 ;
 IF rt.GetString (ADR(Suchwort), Maxzahl,
                  ADR("Bitte geben Sie das zu suchende Wort ein. "),
                  NIL, ADDRESS(RequesterTags))
 THEN
  Prozent := 100 ;
  IF rt.GetLong (Prozent, ADR("Intelligenz"), NIL, HilfsZ)
  THEN
   WHILE ((NOT gefunden) AND (L1 # NIL))
   DO
    L1 := Liste(FindeWortI (L1, Suchwort, Prozent)) ;
    IF (L1 # NIL)
    THEN
     WortundAequivReqausgeben (L1, "Das gesuchte Wort lautet:\n\n") ;
     Wahl := rts.EZRequest (ADR("Weitersuchen ?"),ADR("_Ja|_Nein"), NIL,
                            ADDRESS(RequesterTags), NIL) ;
     gefunden := NOT (Wahl = 1) ;
    END ; (* IF *)
   END ; (* WHILE *)

   IF (NOT gefunden)
   THEN
    rts.vEZRequest (ADR("Es konnte kein (weiteres) Wort gefunden werden."),
                    ADR("Weiter"),
                    NIL, ADDRESS(RequesterTags), NIL) ;
   END ;
  END ;
 END ;
END Wortsuchen ;

PROCEDURE ZausBDListeloeschen (VAR QuellZ : ZeigeraufBDE;
                                   VerweisZ : ZeigeraufEintrag) ;

VAR BDZ, BDZ2 : ZeigeraufBDE ;

BEGIN
 IF (QuellZ # NIL)
 THEN
  IF (QuellZ^.Zeiger = VerweisZ) (* ein Eintrag ist immer vorhanden ! *)
  THEN
   BDZ := QuellZ ; (* vordersten Eintrag herausloeschen *)
   QuellZ := BDZ^.Naechster ;
   Heap.Deallocate (BDZ) ;
  ELSE (* es kann jeder Eintrag nur einmal vorhanden sein *)
   BDZ := QuellZ^.Naechster ;
   WHILE (BDZ # NIL) AND (BDZ^.Naechster # NIL)
   DO
    IF (BDZ^.Naechster^.Zeiger = VerweisZ)
    THEN
     BDZ2 := BDZ^.Naechster^.Naechster ;
     Heap.Deallocate (BDZ^.Naechster) ;
     BDZ^.Naechster := BDZ2 ;
    END ;
    BDZ := BDZ^.Naechster ;
   END ;
  END ;
 END ;
END ZausBDListeloeschen ;

(*PROCEDURE WortundVerkettungloeschen (VAR WortZ : ZeigeraufEintrag) ; FORWARD ;*)

PROCEDURE LoescheVerweisaufEintrag (VAR EintragZ : ZeigeraufEintrag);

VAR BDListeZ : ZeigeraufBDE ;

BEGIN
 IF (EintragZ^.BDListe # NIL)
 THEN
  BDListeZ := EintragZ^.BDListe ;
  WHILE BDListeZ # NIL
  DO
   ZausBDListeloeschen (BDListeZ^.Zeiger^.BDListe, EintragZ) ;
   IF (BDListeZ^.Zeiger^.BDListe = NIL)
   THEN
    WortundVerkettungloeschen (BDListeZ^.Zeiger) ;
   END ;
   BDListeZ := BDListeZ^.Naechster ;
  END ;
 END ;
END LoescheVerweisaufEintrag ;

PROCEDURE WortundVerkettungloeschen (VAR WortZ : ZeigeraufEintrag) ;

(* Diese Prozedur soll aus L1 das übergebene Wort loeschen und sämtliche *)
(* Querverweise von diesem Wort und auf dieses Wort beseitigen *)

VAR Naechster, Letzter : ZeigeraufEintrag ;

BEGIN
 (* Lösungsidee : *)
 (* Zeiger auf nächsten Listeneintrag holen *)
 (* Verweis in diesem Listeneintrag korrigieren *)
 (* Zeiger auf vorherigen Listeneintrag holen *)
 (* Verweis in diesem Listeneintrag korrigieren *)
 (* Bedeutungsliste durchgehen und Wort aus den Bedeutungslisten der *)
 (* hier eingetragenen Wörter loeschen *)
 (* Falls in diesen Bedeutungslisten kein anderer Eintrag als auf dieses *)
 (* Wort vorhanden ist, Einträge ebenfalls loeschen (rekursiv ?) *)
 (* zuletzt eigentliche Wortstruktur freigeben (alle Verweise sind jetzt *)
 (* beseitigt *)
IF WortZ # NIL
THEN
 Naechster := WortZ^.Naechster ;
 Letzter := WortZ^.Letzter ;
 Letzter^.Naechster := Naechster ;
 IF Naechster # NIL THEN
  Naechster^.Letzter := Letzter ;
 END ;
 LoescheVerweisaufEintrag (WortZ) ;
 IF WortZ^.Wortdaten.Typ = Fremdwort
 THEN
  DEC(FWZahl)
 ELSE
  DEC(BDZahl)
 END ;
 WortausStatloeschen (WortZ) ;
 Heap.Deallocate (WortZ) ;
ELSE
 Fehlermeldung (ZeigerFehler) ;
END ;
END WortundVerkettungloeschen ;

PROCEDURE Wortloeschen (VAR L1, L2 : Liste) ;

VAR Eingabe : STRING;
    Zeiger  : ZeigeraufEintrag ;

BEGIN
 Eingabe := "" ;
 IF rt.GetString (ADR(Eingabe), Maxzahl,
                   ADR("Bitte ein Wort eingeben: "),
                   NIL, ADDRESS(RequesterTags))
 THEN
  Zeiger := FindeWort (L1, Eingabe) ;
  IF (Zeiger = NIL)
  THEN
   Zeiger := FindeWort (L2, Eingabe) ;
  END ;
  IF (Zeiger # NIL) THEN
   WortundAequivReqausgeben (Zeiger, "Folgendes Wort wurde "+
                             "zum Löschen vorgesehen:\n\n") ;
   IF Sicherheitsabfrage ("")
   THEN
    gesichert := FALSE ;
    WortundVerkettungloeschen (Zeiger) ;
   END ;
  ELSE
   rts.vEZRequest (ADR("Wort konnte in keiner der beiden Listen gefunden werden."),
                   ADR("Weiter"), NIL, ADDRESS(RequesterTags), NIL) ;
  END ;
 END ;
END Wortloeschen ;

PROCEDURE Wortaendern (VAR LF, LD : Liste) ;

VAR Eingabe, Buffer : STRING ;
    Zeiger, Zeiger2 : ZeigeraufEintrag ;
    BDLEintragZ     : ZeigeraufBDE ;
    Status          : BOOLEAN ;

BEGIN
 Eingabe := "" ;
 IF rt.GetString (ADR(Eingabe), Maxzahl,
                  ADR("Bitte ein Wort eingeben: "),
                  NIL, ADDRESS(RequesterTags))
 THEN
  Zeiger := FindeWort (LD, Eingabe) ;
  IF (Zeiger = NIL)
  THEN
   Zeiger := FindeWort (LF, Eingabe) ;
  END ;
  IF (Zeiger # NIL) THEN
   WortundAequivReqausgeben (Zeiger, "Folgendes Wort wurde "+
                             "zum Ändern vorgesehen:\n\n") ;
   Buffer := Eingabe ;
   IF rt.GetString (ADR(Eingabe), Maxzahl,
                    ADR("Bitte Wort ändern: "),
                    NIL, ADDRESS(RequesterTags))
      AND NOT Vgl (Eingabe, Buffer)
   THEN
    gesichert := FALSE ;
    IF Zeiger^.Wortdaten.Typ = Fremdwort THEN
     Zeiger2 := Worthinzufuegen (LF, Eingabe) ;
    ELSE
     Zeiger2 := Worthinzufuegen (LD, Eingabe) ;
    END ;
    BDLEintragZ := Zeiger^.BDListe ;
    WHILE BDLEintragZ # NIL DO
     IF Zeiger^.Wortdaten.Typ = Fremdwort THEN
      Status := Bedeutungzuordnen (LD, Zeiger2, BDLEintragZ^.Zeiger^.Wortdaten.Wort) ;
     ELSE
      Status := Bedeutungzuordnen (LF, Zeiger2, BDLEintragZ^.Zeiger^.Wortdaten.Wort) ;
     END;
     BDLEintragZ := BDLEintragZ^.Naechster ;
    END ; (* nach und nach alle Bedeutungen dem neuen Wort zuordnen *)
    IF Status THEN (* alles gutgegangen *)
     WortundVerkettungloeschen (Zeiger) ;
    ELSE
     WortundVerkettungloeschen (Zeiger2) ;
    END ;
   END ;
  ELSE
   rts.vEZRequest (ADR("Wort konnte in keiner der beiden Listen gefunden werden"),
                   ADR("Weiter"), NIL, ADDRESS(RequesterTags), NIL) ;
  END ;
 END ;
END Wortaendern ;

PROCEDURE Wortlisteloeschen (VAR L : Liste) ;

VAR Zeiger, Zeiger2 : ZeigeraufEintrag ;

BEGIN
 IF L # NIL THEN
  gesichert := FALSE ;
  Zeiger := L ;
  WHILE (Zeiger^.Naechster # NIL)
  DO
   Zeiger2 := Zeiger^.Naechster^.Naechster ;
   WortausStatloeschen (Zeiger^.Naechster) ;
   Heap.Deallocate (Zeiger^.Naechster) ;
   Zeiger^.Naechster := Zeiger2 ;
  END ;
  BDZahl := 0 ;
  FWZahl := 0 ;
 END ;
END Wortlisteloeschen ;

PROCEDURE NeuesWorteingeben (VAR L1, L2 : Liste;
                                 ScreenZ : ScreenPtr);

(* L1 : in diese Liste wird das neue Wort eingefuegt *)
(* L2 : hier werden die Bedeutungen eingefuegt *)

VAR ZeigerF       : ZeigeraufEintrag ;
    WinTitle      : STRING ;
    WinParm       : ExtNewWindow ;
    WinZ          : WindowPtr ;
    MesZ          : IntuiMessagePtr ;
    MesCopy       : IntuiMessage ;
    adr           : ADDRESS ;
    adr2          : GadgetPtr ;
    FWort, Bed    : STRING ;
    l, c, x, y    : INTEGER ;
    tagbuf        : tagtype ;

BEGIN
 IF L1^.Wortdaten.Typ = Fremdwort
 THEN
  WinTitle := "Neue Fremdwörter eingeben - Taskadresse: " ;
 ELSE
  WinTitle := "Neue deutsche Wörter eingeben - Taskadresse: ";
 END ;
 InitWindowParm (WinParm, WinTitle, ScreenZ, tagbuf, 13) ;
 Windows.OpenWindow (WinZ, WinParm.nw.leftEdge, WinParm.nw.topEdge,
                           WinParm.nw.width,    WinParm.nw.height ,
                           WinTitle,
                           Windows.WinGadSet{sizing, moving, arranging,
                                             closing}) ;
 IF WinZ # NIL THEN
  IntuiL.RefreshWindowFrame (WinZ) ;
  Windows.GetSize (WinZ, l, c) ;
  LOOP
   FWort := "" ;
   IF rt.GetString (ADR(FWort), Maxzahl,
                    ADR("Bitte ein Wort eingeben: "),
                    NIL, ADDRESS(RequesterTags))
    (* ReqTools String - Requester, mittig *)
   THEN
    Windows.GetPos (WinZ, y, x) ;
    IF (y >= l) THEN Windows.Clear (WinZ) ; Windows.SetPos (WinZ, 0, 0) ; END ;
    Windows.WriteS (WinZ, "Als Wort wurde ") ;
    Windows.WriteS (WinZ, FWort);
    Windows.WriteS (WinZ, " eingegeben.") ;
    Windows.WriteL (WinZ) ;
    Windows.GetPos (WinZ, y, x) ;

    Bed := "" ;
    IF rt.GetString (ADR(Bed), Maxzahl,
                     ADR("Bitte eine Bedeutung eingeben: "),
                     NIL, ADDRESS(RequesterTags))
    (* ReqTools String - Requester, mittig *)
    THEN
     IF (y >= l) THEN Windows.Clear (WinZ) ; Windows.SetPos (WinZ, 0, 0) ; END ;
     Windows.WriteS (WinZ, "Als Bedeutung wurde ") ;
     Windows.WriteS (WinZ, Bed) ;
     Windows.WriteS (WinZ, " eingegeben.") ;
     Windows.WriteL (WinZ) ;
     Windows.GetPos (WinZ, y, x) ;

     gesichert := FALSE ;
     ZeigerF := Worthinzufuegen (L1, FWort); (* Wort hinzufügen *)
     IF (ZeigerF # NIL)
     THEN
      IF Bedeutungzuordnen (L2, ZeigerF, Bed)
      THEN
       Bed := "" ;
       WHILE rt.GetString (ADR(Bed), Maxzahl,
                           ADR("Bitte eine Bedeutung eingeben: "),
                           NIL, ADDRESS(RequesterTags))
        (* ReqTools String - Requester, mittig *)
       DO
        IF (y >= l) THEN Windows.Clear (WinZ) ; Windows.SetPos (WinZ, 0, 0) ; END ;
        Windows.WriteS (WinZ, "Als Bedeutung wurde ") ;
        Windows.WriteS (WinZ, Bed) ;
        Windows.WriteS (WinZ, " eingegeben.") ;
        Windows.WriteL (WinZ) ;
        Windows.GetPos (WinZ, y, x) ;

        IF NOT Bedeutungzuordnen (L2, ZeigerF, Bed)
        THEN
         Fehlermeldung (Speichermangel) ;
        END ;
        Bed := "" ;
       END; (* von WHILE *)
       IF (y >= l) THEN Windows.Clear (WinZ) ; Windows.SetPos (WinZ, 0, 0) ; END ;
       WortundAequivausgeben (WinZ, ZeigerF, c, l) ;
       Windows.GetPos (WinZ, y, x) ;

      ELSE
       Fehlermeldung (Speichermangel) ;
       WortundVerkettungloeschen (ZeigerF) ;
      END ;
     ELSE
      Fehlermeldung (Speichermangel) ;
     END ;
    END ; (* keine Eingabe bei Bedeutung, Liste nicht verändert *)
   ELSE (* keine Eingabe bei Fremdwort *)
    EXIT ;
   END ;
  END ; (* LOOP *)
  Windows.WriteL (WinZ) ;
  Windows.WriteS (WinZ, "Schließsymbol oder ESC zum Schließen des Fensters!") ;
  IntuiL.ModifyIDCMP (WinZ, IDCMPFlagSet{closeWindow, rawKey}) ;
  (* Tastaturabfrage in Fenster aktivieren *)
  IntuiL.ActivateWindow (WinZ) ;
  REPEAT
   MesCopy.class := IDCMPFlagSet{} ;
   ExecL.WaitPort (WinZ^.userPort) ; (* auf Nachricht von Intuition warten *)
   MesZ := ExecL.GetMsg (WinZ^.userPort) ;
   IF MesZ#NIL
   THEN
    MesCopy := MesZ^ ;
    ExecL.ReplyMsg (MesZ) ;
    IF (rawKey IN MesCopy.class)
    THEN
     IF (MesCopy.code = Esc)
     THEN INCL (MesCopy.class, closeWindow) ;
     END ;
    END ;
   END ; (* IF *)
  UNTIL (closeWindow IN MesCopy.class) ;
  Windows.CloseWindow (WinZ) ;
  WinZ := NIL ;
 ELSE
  Fehlermeldung (nixFenster) ;
 END ;
END NeuesWorteingeben;

PROCEDURE ListeauswaehlenundWoertereingeben (VAR LD, LF  : Liste;
                                                 ScreenZ : ScreenPtr) ;

BEGIN
 IF Listewaehlen ("In welche Liste neue Wörter eingeben ?") THEN
  NeuesWorteingeben (LD, LF, ScreenZ) ;
 ELSE
  NeuesWorteingeben (LF, LD, ScreenZ) ;
 END ;
END ListeauswaehlenundWoertereingeben ;

PROCEDURE ListeabxausgebenoB (VAR WinZ : WindowPtr; L : Liste ;
                                  Nr : LONGCARD ;
                                  x, y, width, height : INTEGER) ;

VAR ListenEintragZ : ZeigeraufEintrag ;
    RastPortZ      : RastPortPtr ;

BEGIN
 RastPortZ := WinZ^.rPort ;
 ListenEintragZ := Wortnummerxholen (L, Nr, sehrgut) ;
 WHILE ((y+8) <= height)
 DO
  GraphicsL.Move (RastPortZ, x, y) ;
  GraphicsL.Text (RastPortZ, ADR(ListenEintragZ^.Wortdaten.Wort),
                  String.Length (ListenEintragZ^.Wortdaten.Wort)) ;
  ListenEintragZ := ListenEintragZ^.Naechster ;
  INC (y, 9) ;
 END ; (* WHILE *)
END ListeabxausgebenoB ;

PROCEDURE Zufall (Zahl : LONGCARD) : LONGCARD;

VAR Zufallsausdruck : REAL;
    Zufallszahl     : LONGCARD;

BEGIN
 IF Zahl # 0 THEN
  REPEAT
   Zufallsausdruck := (Random()*FLOAT(Zahl)+0.5);
   Zufallszahl := TRUNC(Zufallsausdruck);
   IF (FRAC(Zufallsausdruck) >= 0.5) THEN INC(Zufallszahl);
   END; (* von IF *)
  UNTIL Zufallszahl # letzteWahl ;
 ELSE
  Zufallszahl := 0 ;
  letzteWahl := Zufallszahl ;
 END ;
 RETURN Zufallszahl;
END Zufall;

PROCEDURE Lernerfolgzuruecksetzen (VAR L : Liste) ;

VAR Zeiger : ZeigeraufEintrag ;

BEGIN
 Zeiger := L^.Naechster ;
 WHILE Zeiger # NIL
 DO
  WortAbstufen(Zeiger) ;
  Zeiger := Zeiger^.Naechster ;
 END ; (* WHILE *)
END Lernerfolgzuruecksetzen ;

PROCEDURE Wortauswaehlenundabfragen (VAR LF, LD      : Liste;
                                         Reihenfolge : AbfrageTyp;
                                         ScreenZ     : ScreenPtr);

VAR Zeiger                     : ZeigeraufEintrag ;
    BedZeiger                  : ZeigeraufBDE ;
    ZielLernerfolg, Lernerfolg : Lerntyp ;
    Eingabe, Ausgabe           : STRING ;
    Status                     : (Abbruch, Weiter) ;
    Listentyp                  : Unterscheidungstyp ;
    WahlFW, WahlBD, Wahl       : VzahlTyp ;
    L                          : Liste ;
    tagbuf                     : tagtype ;
    HilfsZ                     : ADDRESS ;

 PROCEDURE Richtig ;

 BEGIN
  WortHochstufen (Zeiger) ;
  SetSignal (CopperlistZ1 (* global *), ScreenZ, TRUE) ;
  IF Prefs.BedeutungbeirichtigerAntwort THEN
   WortundAequivReqausgeben (Zeiger, ">>> RICHTIG <<<\n\n") ;
  ELSE DosL.Delay (5) ;
  END ;
  RemoveSignal (ScreenZ) ;
 END Richtig ;

 PROCEDURE Falsch ;

 BEGIN
  WortAbstufen (Zeiger) ; (* Wort wieder auf ungenuegend setzen*)
  SetSignal (CopperlistZ2, ScreenZ, FALSE) ;
  WortundAequivReqausgeben (Zeiger, ">>> LEIDER FALSCH ! <<<\n\n") ;
  RemoveSignal (ScreenZ) ;
 END Falsch ;

BEGIN
 Status := Weiter ;
 ZielLernerfolg := Lernerfolgabfragen();
 Lernerfolg := MIN (Lerntyp) ; INC (Lernerfolg) ;
 REPEAT
  CASE Reihenfolge
  OF
  | DeutschZufall      : L := LD ; Wahl := Zufall (BDZahl) ;
                         Zeiger := Wortnummerxholen (L, Wahl, Lernerfolg) ;
  | DeutschReihenfolge : L := LD ;
                         INC (WahlBD) ;
                         IF WahlBD > BDZahl
                         THEN
                          WahlBD := 1 ;
                         END ;
                         Wahl := WahlBD ;
                         Zeiger := Wortnummerxholen (L, Wahl, Lernerfolg) ;
  | FremdZufall        : L := LF ; Wahl := Zufall (FWZahl) ;
                         Zeiger := Wortnummerxholen (L, Wahl, Lernerfolg) ;
  | FremdReihenfolge   : L := LF ;
                         INC(WahlFW) ;
                         IF WahlFW > FWZahl
                         THEN
                          WahlFW := 1 ;
                         END ;
                         Wahl := WahlFW ;
                         Zeiger := Wortnummerxholen (L, Wahl, Lernerfolg) ;
  | AllesZufall        : IF L = NIL
                         THEN
                          L := LF ;
                          Wahl := Zufall (FWZahl) ;
                         ELSE
                          IF L = LF
                          THEN L := LD ; Wahl := Zufall (BDZahl) ;
                          ELSE L := LF ; Wahl := Zufall (FWZahl) ;
                          END ;
                         END ;
                         Zeiger := Wortnummerxholen (L, Wahl, Lernerfolg) ;
                         IF Zeiger = NIL
                         THEN
                          IF L = LF
                          THEN L := LD ; Wahl := Zufall (BDZahl) ;
                          ELSE L := LF ; Wahl := Zufall (FWZahl) ;
                          END ;
                          Zeiger := Wortnummerxholen (L, Wahl, Lernerfolg) ;
                         END ;
  END ; (* CASE *)

  IF Zeiger = NIL
  THEN
   IF Lernerfolg = ZielLernerfolg
   THEN
    Status := Abbruch ;
    rts.vEZRequest (ADR("Liste noch lehr oder kein Wort dieser Lernstufe mehr\n"+
                        "vorhanden!"),
                    ADR("Weiter"), NIL, ADDRESS(RequesterTags), NIL) ;
   ELSE INC (Lernerfolg) ;
   END ;
  ELSE
   Eingabe := "" ;
   Ausgabe := Zeiger^.Wortdaten.Wort ;
   String.Concat (Ausgabe, "\n\n") ;
   StatistikinString (Ausgabe, StatZ) ;
   HilfsZ := TAG (tagbuf, rt.LockWindowTag, TRUE,
                          rt.Screen, ScreenZ, rt.ReqPos,
                          rt.ReqPosCenterScr, rt.glTextFmt,
                          ADR(Ausgabe),
                          rt.Underscore, "_",
                          rt.gsGadFmt, ADR("_Testen|_keine Ahnung|_Abbruch"),
                  tagEnd) ;

   IF rt.GetString (ADR(Eingabe), Maxzahl,
                    ADR("Vokabelabfrage"),
                    NIL, HilfsZ)
   THEN
    IF Vgl(Eingabe,"")
    THEN (* Tiprunde *)
     Eingabe := " " ;
     Eingabe[0] := Zeiger^.BDListe^.Zeiger^.Wortdaten.Wort[0] ;
     IF rt.GetString (ADR(Eingabe), Maxzahl,
                      ADR("ein kleiner Tip ..."),
                      NIL, ADDRESS(RequesterTags))
     THEN
      IF BDinBDListe (Zeiger^.BDListe, Eingabe) THEN
       WortundAequivReqausgeben (Zeiger, ">>> RICHTIG <<<\n\n") ;
      ELSE
       Falsch ;
      END ;
     ELSE Status := Abbruch ;
     END ;
    ELSE
     BedZeiger := BDinBDListeI(Zeiger^.BDListe, Eingabe) ;

     IF BedZeiger # NIL THEN
      IF BDinBDListe (Zeiger^.BDListe, Eingabe) THEN
       Richtig ;
      ELSE
       IF rt.GetString (ADR(Eingabe), Maxzahl,
                        ADR("Das war doch wohl ein Tippfehler ?"),
                        NIL, ADDRESS(RequesterTags))
       THEN
        IF BDinBDListe (Zeiger^.BDListe, Eingabe) THEN
         WortundAequivReqausgeben (Zeiger, ">>> RICHTIG <<<\n\n") ;
        ELSE
         Falsch ;
        END ;
       ELSE Status := Abbruch ;
       END ;
      END ;
     ELSE
      Falsch ;
     END;
    END ;
   ELSE Status := Abbruch ;
   END; (* von ELSE *)
  END;
 UNTIL Status = Abbruch;
END Wortauswaehlenundabfragen;

PROCEDURE WortTest (LF, LD : Liste; ScreenZ : ScreenPtr) ;

VAR Antwort1, Antwort2 : LONGINT ;
    Reihenfolge        : AbfrageTyp ;

BEGIN
 Antwort1 := rts.EZRequest (ADR("Bitte wählen Sie die Abfragerichtung !"),
                            ADR("_Bedeutungen abfragen|_Fremdwörter abfragen|"+
                                "Beides _abwechselnd"),
                            NIL, ADDRESS(RequesterTags), NIL) ;
 IF Antwort1 # 0
 THEN
  Antwort2 := rts.EZRequest (ADR("Bitte wählen Sie den Abfragemodus !"),
                             ADR("in _Reihenfolge|_zufällig"), NIL,
                             ADDRESS(RequesterTags), NIL) ;
 END ;
 CASE Antwort1
 OF
 | 0 : Reihenfolge := AllesZufall ;
 | 1 : IF Antwort2 = 0 THEN Reihenfolge := DeutschZufall ;
                       ELSE Reihenfolge := DeutschReihenfolge ;
                       END ;
 | 2 : IF Antwort2 = 0 THEN Reihenfolge := FremdZufall ;
                       ELSE Reihenfolge := FremdReihenfolge ;
                       END ;
 END ; (* CASE *)
 Wortauswaehlenundabfragen (LF, LD, Reihenfolge, ScreenZ) ;
END WortTest ;

PROCEDURE Dateinamenerfragen (VAR ScreenZ   : ScreenPtr ;
                              VAR Dateiname : STRING;
                                  Title     : TitleT) : BOOLEAN ;

VAR Status  : BOOLEAN ;
    Pattern : ARRAY [0..40] OF CHAR ;
    Ausgabe : STRING ;

BEGIN
 ScreenZeiger (ScreenZ) ;

 Pattern := "(#?.VOK|#?.prefs|#?.VOK.pp)" ;
 CASE Title OF
 | Save  : Ausgabe := "Bitte Datei zum Sichern auswählen:" ;
 | Load  : Ausgabe := "Bitte Datei zum Laden auswählen:" ;
 | Default : Ausgabe := "Bitte Standardpfad und -datei waehlen:" ;
 ELSE
  Fehlermeldung (CaseIndex) ;
  Ausgabe := "Bitte Datei auswaehlen:" ;
 END ;
 Status := ReqTFileReq.FileReq (Dateiname, ScreenZ,
                                Ausgabe,
                                -1, -1, Pattern, (Title = Save)) ;
 RETURN Status ;
END Dateinamenerfragen ;

(*PROCEDURE Dateidirektspeichern (VAR ScreenZ : ScreenPtr ;
                                VAR L       : Liste;
                                    Notfall : BOOLEAN);

VAR Status    : Antwort;
    Daten     : Datenblock;
    Naechster : ZeigeraufEintrag;
    Zeiger    : ZeigeraufBDE;
    Datei     : File; Dateiname : STRING ;

BEGIN
 Dateiname := Prefs.Vokabelverzeichnis ;
 REPEAT
  Status := Dateioeffnen (ScreenZ, Datei, Dateiname, Notfall);
  IF (Status = Weiter) THEN
   Daten.Wort := ProgTitle;
   Daten.Typ := Fremdwort;
   WriteByteBlock (Datei, Daten);
   IF (Datei.res = done)
   THEN
    Daten.Wort := '';
    Naechster := L^.Naechster;
    WHILE (Naechster # NIL) AND (Status = Weiter) DO
     Daten := Naechster^.Wortdaten;
     WriteByteBlock (Datei, Daten);
     IF (Datei.res = done)
     THEN
      Daten.Wort := '';
      Zeiger := Naechster^.BDListe;
      WHILE (Zeiger # NIL) AND (Status = Weiter) DO
       Daten := Zeiger^.Zeiger^.Wortdaten;
       WriteByteBlock (Datei, Daten);
       IF (Datei.res = done)
       THEN
        Daten.Wort := '';
        Zeiger := Zeiger^.Naechster;
       ELSE DiskFehlermeldung (Datei.res) ; Status := Fehler ;
       END ;
      END;
      Naechster := Naechster^.Naechster;
     ELSE DiskFehlermeldung (Datei.res) ; Status := Fehler ;
     END ;
    END;
   ELSE DiskFehlermeldung (Datei.res) ; Status := Fehler ;
   END ; (* IF *)
  END;
  IF (Status = Weiter)
  THEN
   gesichert := TRUE ;  (* fehlerfreies Speichern *)
   Close (Datei) ;
   Fehlermeldung (Dateigesichert) ;
  ELSE
   Close (Datei);
  END ;
 UNTIL (gesichert = TRUE) OR NOT(Notfall) ;
END Dateidirektspeichern;*)

BEGIN
 FWZahl := 0 ;
 BDZahl := 0 ;
 letzteWahl := 0 ;
 gesichert := TRUE ;
 StatZ := NIL ;
 Color0 := 0 ;
 Color1 := 0 ;
 WinZ := NIL ;
 Heap.Allocate (StatZ, SIZE(StatistikT)) ;
END RESOURCE.

