{ MENU.INC: Menu Routines }

{==========================================================================}
{ (c) Copyright Waterline Software Developent V.O.F. 1990-1994             }
{                                                                          }
{    Waterline Software Development V.O.F.                                 }
{    Wouter Sluislaan 12                                                   }
{    1461 AC  Zuidoostbeemster                                             }
{    The Netherlands                                                       }
{                                                                          }
{ It not allowed to use this user interface in any program not owned by    }
{ the Waterline Software Development V.O.F.                                }
{ Special conditions apply to programs distributed by the Waterline        }
{ Software Development V.O.F. If the source code of any of these programs  }
{ is distributed as well, it is NOT allowed to use the user interface in   }
{ your own programs. Violators may be prosecuted!                          }
{                                                                          }
{ Please contact the Waterline Software Development V.O.F. at the above    }
{ address for your licence to use the "Ramon" user interface. You will get }
{ the most recent copy of the "Ramon" user interface and the "Ramon" user  }
{ interface expert for free. This program helps you design your user       }
{ interfaces at an instance.                                               }
{                                                                          }
{ This copyright notice should remain in this file and all files that are  }
{ part of the user interface "Ramon".                                      }
{==========================================================================}

{ History:

10-06-93 Uitbreiding van MenuSelect met zoektekst. Door een tekst in te
         voeren wordt de cursor op het item gezet dat een match met de
         zoektekst heeft.

17-10-93 MenuKeysLine en MenuSetHelp toegevoegd.

}

TYPE MenuItemRecordPtr = ^MenuItemRecord;
     MenuItemRecord    = RECORD
                               PrevItemRecordPtr : MenuItemRecordPtr;
                               ItemTekst         : STRING;
                               NextItemRecordPtr : MenuItemRecordPtr;
                         END;

     MenuRecordPtr = ^MenuRecord;
     MenuRecord    = RECORD
                           PrevMenuRecordPtr   : MenuRecordPtr;
                           PrevMenuQuit        : BOOLEAN;
                           LbX,LbY,LX,LY       : XYType;
                           Title               : STRING;
                           ItemCount           : BYTE;
                           TopOfMenuItem,
                           PrevTopOfMenuItem   : BYTE;
                           SelectBarOffset,
                           PrevSelectBarOffset : BYTE;
                           FirstItemRecordPtr  : MenuItemRecordPtr;
                           IsInActiveColors    : BOOLEAN;
                           HelpHandle          : HelpHandleType;
                     END;

CONST CurrMenuRecordPtr : MenuRecordPtr = NIL;


{--------------------------------------------------------------------------}
{ MenuTitle                                                                }
{                                                                          }
{ Hiermee wordt een menu gedefinieerd. De X en Y coordinaten zijn van de   }
{ linker bovenhoek. -1 en -1 geven het midden van het scherm aan. De tekst }
{ komt in een kader bovenaan het menu te staan, maar dat doet MenuShow.    }
{ Voor dit nieuwe menu wordt een nieuw record op de heap aangemaakt en de  }
{ juiste gegevens alvast ingevuld.                                         }
{                                                                          }
PROCEDURE MenuDefine (X,Y : XYType; KopTekst : STRING);

VAR NewMenuRecordPtr : MenuRecordPtr;

BEGIN
     KopTekst:=' '+KopTekst+' ';

     GetMem (NewMenuRecordPtr,SizeOf (MenuRecord));

     WITH NewMenuRecordPtr^ DO
     BEGIN
          PrevMenuRecordPtr:=CurrMenuRecordPtr;
          LbX:=X;
          LbY:=Y;
          LX:=Length (KopTekst)+2;                         { ivm zijlijnen }
          LY:=4;                     { ivm het kopje dat er nog boven moet }
          Title:=KopTekst;
          ItemCount:=0;
          TopOfMenuItem:=1;
          PrevTopOfMenuItem:=0;
          SelectBarOffset:=1;
          PrevSelectBarOffset:=0;
          FirstItemRecordPtr:=NIL;
          IsInActiveColors:=FALSE;
          HelpHandle:=0;
     END; { with }

     CurrMenuRecordPtr:=NewMenuRecordPtr;
END;


{--------------------------------------------------------------------------}
{ MenuSetHelp                                                              }
{                                                                          }
{ Met deze routine kan een help link voor het hele menu ingesteld worden.  }
{                                                                          }
PROCEDURE MenuSetHelp (Handle : HelpHandleType);
BEGIN
     IF (CurrMenuRecordPtr <> NIL) THEN
        CurrMenuRecordPtr^.HelpHandle:=Handle;
END;


{--------------------------------------------------------------------------}
{ MenuAddItem                                                              }
{                                                                          }
{ Hiermee kan een item aan de menu opties toegevoegd worden. Het wordt de  }
{ laatste optie van het menu.                                              }
{                                                                          }
PROCEDURE MenuAddItem (Tekst : STRING);

VAR NewItemRecordPtr,
    CurrItemRecordPtr : MenuItemRecordPtr;

BEGIN
     IF (CurrMenuRecordPtr = NIL) THEN Exit;

     Tekst:=' '+Tekst+' ';

     GetMem (NewItemRecordPtr,SizeOf (MenuItemRecord));

     WITH NewItemRecordPtr^ DO
     BEGIN
          ItemTekst:=Tekst;
          NextItemRecordPtr:=NIL;
     END; { with }

     WITH CurrMenuRecordPtr^ DO
     BEGIN
          Inc (ItemCount);
          Inc (LY);

          IF (LX-2 < Length (Tekst)) THEN
          BEGIN
               LX:=2+Length (Tekst);

               CurrItemRecordPtr:=CurrMenuRecordPtr^.FirstItemRecordPtr;
               WHILE (CurrItemRecordPtr <> NIL) DO
                     WITH CurrItemRecordPtr^ DO
                     BEGIN
                          IF (Length (ItemTekst) < LX-2) THEN
                             ItemTekst:=ItemTekst+RepChar (LX-2-Length (ItemTekst),' ');

                          CurrItemRecordPtr:=NextItemRecordPtr;
                     END; { with, while }
          END;

          IF (Length (Tekst) < LX-2) THEN
             WITH NewItemRecordPtr^ DO
                  ItemTekst:=ItemTekst+RepChar (LX-2-Length (ItemTekst),' ');
     END; { with }

     IF (CurrMenuRecordPtr^.FirstItemRecordPtr = NIL) THEN
     BEGIN
          CurrMenuRecordPtr^.FirstItemRecordPtr:=NewItemRecordPtr;
          NewItemRecordPtr^.PrevItemRecordPtr:=NIL;
     END ELSE
     BEGIN
          CurrItemRecordPtr:=CurrMenuRecordPtr^.FirstItemRecordPtr;
          WHILE (CurrItemRecordPtr^.NextItemRecordPtr <> NIL) DO
                CurrItemRecordPtr:=CurrItemRecordPtr^.NextItemRecordPtr;

          NewItemRecordPtr^.PrevItemRecordPtr:=CurrItemRecordPtr;
          CurrItemRecordPtr^.NextItemRecordPtr:=NewItemRecordPtr;
     END;
END;


{--------------------------------------------------------------------------}
{ MenuSetFirst                                                             }
{                                                                          }
{ Met deze routine kan de regel ingesteld worden waarop de cursor bij de   }
{ volgende MenuSelect komt te staan.                                       }
{                                                                          }
PROCEDURE MenuSetFirst (Nr : BYTE);
BEGIN
     CurrMenuRecordPtr^.SelectBarOffset:=Nr;
END;


{--------------------------------------------------------------------------}
{ MenuSetActive                                                            }
{                                                                          }
{ Zet het huidige menu in de active kleuren.                               }
{                                                                          }
PROCEDURE MenuSetActive;
BEGIN
     WITH CurrMenuRecordPtr^ DO
          IF (NOT IsInActiveColors) THEN
          BEGIN
               SetLines (Double);
               BoxSetActive (LbX,LbY,LX,LY);
               WriteXY (LbX,LbY+2,TL+RepChar (LX-2,HO)+TR);
               ChangeColor (LbX+1,LbY+1,LX-2,cBoxLinesActive);
               IsInActiveColors:=TRUE;
          END; { if,with }
END;


{--------------------------------------------------------------------------}
{ MenuSetInactive                                                          }
{                                                                          }
{ Zet het huidige menu in de in-actieve kleuren.                           }
{                                                                          }
PROCEDURE MenuSetInactive;
BEGIN
     WITH CurrMenuRecordPtr^ DO
          IF IsInActiveColors THEN
          BEGIN
               SetLines (Double);
               BoxSetInactive (LbX,LbY,LX,LY);
               WriteXY (LbX,LbY+2,TL+RepChar (LX-2,HO)+TR);
               ChangeColor (LbX+1,LbY+1,LX-2,cBoxLinesInactive);
               IsInActiveColors:=FALSE;
          END; { if,with }
END;


{--------------------------------------------------------------------------}
{ MenuItemCount                                                            }
{                                                                          }
{ Deze routine geeft het aantal toegevoegde items terug.                   }
{                                                                          }
FUNCTION MenuItemCount : BYTE;
BEGIN
     MenuItemCount:=CurrMenuRecordPtr^.ItemCount
END;


{--------------------------------------------------------------------------}
{ MenuShow                                                                 }
{                                                                          }
{ Hiermee wordt het huidige menu op het scherm gezet. Het onderliggende    }
{ schermdeel wordt dmv van de window routines bewaard om later door Menu   }
{ Erase terug gezet te worden. De items worden nog niet in het scherm      }
{ gezet, dan doet MenuSelect omdat er ook gescrolled kan worden en die     }
{ routine daarom toch moet kunnen scrollen.                                }
{                                                                          }
PROCEDURE MenuShow;

VAR Lp : BYTE;

BEGIN
     CurrMenuRecordPtr^.PrevMenuQuit:=MenuQuit;
     MenuQuit:=FALSE;

     WITH CurrMenuRecordPtr^ DO
     BEGIN
          IF (LbY = 255) THEN
             LbY:=(Video.Rows DIV 2)-(LY DIV 2);

          WindowPush (LbX,LbY,LX,LY);
          MenuSetActive;

          WriteXY (LbX+1,LbY+1,RepChar (LX-2,' '));
          WriteXY (LbX+1+(LX-2-Length (Title)) DIV 2,LbY+1,Title);
     END; { with }
END;


{--------------------------------------------------------------------------}
{ MenuErase                                                                }
{                                                                          }
{ Hiermee wordt het laatste menu van het scherm verwijderd en van de heap  }
{ af gehaald. Het scherm wordt hersteld.                                   }
{                                                                          }
PROCEDURE MenuErase;

VAR EraseMenuRecordPtr : MenuRecordPtr;
    CurrItemRecordPtr,
    EraseItemRecordPtr : MenuItemRecordPtr;

BEGIN
     IF (CurrMenuRecordPtr = NIL) THEN Exit;

     WindowPop;

     CurrItemRecordPtr:=CurrMenuRecordPtr^.FirstItemRecordPtr;
     IF (CurrItemRecordPtr <> NIL) THEN
        WHILE (CurrItemRecordPtr^.NextItemRecordPtr <> NIL) DO
              CurrItemRecordPtr:=CurrItemRecordPtr^.NextItemRecordPtr;

     WHILE (CurrItemRecordPtr <> NIL) DO
     BEGIN
          EraseItemRecordPtr:=CurrItemRecordPtr;
          CurrItemRecordPtr:=CurrItemRecordPtr^.PrevItemRecordPtr;
          FreeMem (EraseItemRecordPtr,SizeOf (MenuItemRecord));
     END;

     MenuQuit:=CurrMenuRecordPtr^.PrevMenuQuit;

     EraseMenuRecordPtr:=CurrMenuRecordPtr;
     CurrMenuRecordPtr:=CurrMenuRecordPtr^.PrevMenuRecordPtr;
     FreeMem (EraseMenuRecordPtr,SizeOf (MenuRecord));
END;


{--------------------------------------------------------------------------}
{ MenuPrintItems                                                           }
{                                                                          }
{ Deze routine drukt de items van een menu af in het window dat al op het  }
{ scherm staat. Als de TopOfMenuItem en PrevTopOfMenuItem verschillen, dan }
{ is het window gescrollt en moet het helemaal opnieuw gevuld worden. Dat  }
{ gebeurt automatisch aan het begin omdat door MenuDefine de PrevTopOfM... }
{ op 0 is gezet.                                                           }
{ Als dit niet hoeft, dan wordt gekeken naar de SelectBarOffset en de Prev }
{ versie daarvan. Als deze verschillen dan wordt de huidige verwijderd en  }
{ de nieuwe neergezet. Als de Prev 0 is, dan is er nog geen scrollbar op   }
{ het scherm aanwezig (door een Full Draw) en wordt ie ook niet verwijderd.}
{                                                                          }
PROCEDURE MenuPrintItems;

VAR CurrItemRecordPtr : MenuItemRecordPtr;
    Lp                : BYTE;

BEGIN
     WITH CurrMenuRecordPtr^ DO
     BEGIN
          IF (PrevTopOfMenuItem <> TopOfMenuItem) THEN
          BEGIN
               CurrItemRecordPtr:=FirstItemRecordPtr;
               IF (TopOfMenuItem > 1) THEN
                  FOR Lp:=1 TO TopOfMenuItem DO
                      CurrItemRecordPtr:=CurrItemRecordPtr^.NextItemRecordPtr;

               SetColor (cBoxData);

               FOR Lp:=1 TO LY-4 DO
               BEGIN
                    WriteXY (LbX+1,LbY+2+Lp,CurrItemRecordPtr^.ItemTekst);
                    CurrItemRecordPtr:=CurrItemRecordPtr^.NextItemRecordPtr;
               END;

               PrevTopOfMenuItem:=TopOfMenuItem;
               PrevSelectBarOffset:=0;       { forceer afdrukken selectbar }
          END;

          IF (SelectBarOffset <> PrevSelectBarOffset) THEN
          BEGIN
               IF (PrevSelectBarOffset <> 0) THEN
                  ChangeColor (LbX+1,LbY+2+PrevSelectBarOffset,LX-2,cBoxData);

               ChangeColor (LbX+1,LbY+2+SelectBarOffset,LX-2,cMenuSelectBar);

               PrevSelectBarOffset:=SelectBarOffset;
          END;
     END; { with }
END;


{--------------------------------------------------------------------------}
{ MenuSelect                                                               }
{                                                                          }
{ Hiermee kan een optie uit het menu gekozen worden. Met de pijl toetsen   }
{ omhoog en omlaag kan de cursor balk over de verschillende opties bewo-   }
{ gen worden. Met Enter kan een keuze gemaakt worden en Escape is afbreken }
{ om terug te keren naar het vorige menu. Met de muis kan een van de menu  }
{ opties aangeklikt worden.                                                }
{                                                                          }
FUNCTION MenuSelect : KeyType;

VAR Quit     : BOOLEAN;
    Search,
    Check    : STRING[80];
    Lp       : BYTE;
    ReSearch : BOOLEAN;

    SearchItemRecordPtr : MenuItemRecordPtr;
    SearchItemNr        : WORD;

BEGIN
     IF (CurrMenuRecordPtr = NIL) THEN Exit;

     Search:='';
     ReSearch:=FALSE;
     MenuSetActive;

     PushKeysLine;
     WriteKeysLine (MenuKeysLine);

     Quit:=FALSE;
     REPEAT
           WHILE ReSearch DO
           BEGIN
                SearchItemRecordPtr:=CurrMenuRecordPtr^.FirstItemRecordPtr;
                SearchItemNr:=1;

                WHILE (SearchItemRecordPtr <> NIL) DO
                      WITH SearchItemRecordPtr^ DO
                      BEGIN
                           Check:=UpCaseString (Copy (ItemTekst,2,Length (Search)));
                           FOR Lp:=1 TO Length (Search) DO
                               IF (Search[Lp] = #255{wildcard}) THEN
                                  Check[Lp]:=#255; { gelijk maken }

                           IF (Check = Search) THEN
                              WITH CurrMenuRecordPtr^ DO
                              BEGIN
                                   SelectBarOffset:=SearchItemNr;
                                   SearchItemNr:=0; { ter indicatie "gevonden" }
                                   Break; { uit de while }
                              END;

                           SearchItemRecordPtr:=NextItemRecordPtr;
                           Inc (SearchItemNr);
                      END; { with,while }

                ReSearch:=FALSE;

                IF (SearchItemNr <> 0) THEN
                BEGIN
                     IF (Length (Search) < CurrMenuRecordPtr^.LX-2) THEN
                     BEGIN
                          { wildcard toevoegen }
                          Search:=Search+Search[Length (Search)];
                          Search[Length (Search)-1]:=#255;
                          ReSearch:=TRUE; { nog eens proberen }
                     END ELSE
                     BEGIN
                          Delete (Search,Length (Search),1);
                          WHILE (Search <> '') AND (Search[Length (Search)] = #255) DO
                                Delete (Search,Length (Search),1);

                          Write (#7);
                     END;
                END;

           END; { while }

           MenuPrintItems;

           IF (Search <> '') THEN
           BEGIN
                WITH CurrMenuRecordPtr^ DO
                     CursorGotoXY (LbX+Length (Search)+1,LbY+2+SelectBarOffset);

                CursorOn;
                Key:=ReadKey;
                CursorOff;
           END ELSE
               Key:=ReadKey;

           CASE Key OF
                kUp : WITH CurrMenuRecordPtr^ DO
                          IF (SelectBarOffset > 1) THEN
                          BEGIN
                               Dec (SelectBarOffset);
                               Search:='';
                          END;

                kDown : WITH CurrMenuRecordPtr^ DO
                             IF (SelectBarOffset < LY-4) THEN
                             BEGIN
                                  Inc (SelectBarOffset);
                                  Search:='';
                             END;

                kPgUp,
                kCtrlPgUp,
                kHome     : BEGIN
                                 CurrMenuRecordPtr^.SelectBarOffset:=1;
                                 Search:='';
                            END;

                kPgDn,
                kCtrlPgDn,
                kEnd      : WITH CurrMenuRecordPtr^ DO
                            BEGIN
                                 SelectBarOffset:=LY-4;
                                 Search:='';
                            END;

                kEsc,
                kPressedRButton : BEGIN
                                       Key:=kEsc;
                                       Quit:=TRUE;
                                  END;

                kRet : BEGIN
                            Key:=KeyType (CurrMenuRecordPtr^.SelectBarOffset+Byte (mOpt01)-1);
                            Quit:=TRUE;
                       END;

                kBs : IF (Search <> '') THEN
                      BEGIN
                           Delete (Search,Length (Search),1);
                           ReSearch:=(Search <> '');
                      END;

                kF1 : IF (CurrMenuRecordPtr^.HelpHandle <> 0) THEN
                         RequestHelp (CurrMenuRecordPtr^.HelpHandle);

                ELSE
                    IF (AsciiKey IN [' '..'~']) AND
                       (Length (Search) < Video.Cols) THEN {some max.}
                    BEGIN
                         Search:=Search+UpCase (AsciiKey);
                         ReSearch:=TRUE;
                    END;
           END; { case }
     UNTIL Quit;

     WITH CurrMenuRecordPtr^ DO
     BEGIN
          ChangeColor (LbX+1,LbY+2+SelectBarOffset,LX-2,cMenuSelected);
          PrevSelectBarOffset:=0;
          MenuSetInactive;
     END; { with }

     PopKeysLine;

     MenuSelect:=Key;
END;
