{ $Id: menus.pas,v 1.16 2002/10/17 11:24:17 pierre Exp $ }
{********[ SOURCE FILE OF GRAPHICAL FREE VISION ]**********}
{                                                          }
{   System independent GRAPHICAL clone of MENUS.PAS        }
{                                                          }
{   Interface Copyright (c) 1992 Borland International     }
{                                                          }
{   Copyright (c) 1996, 1997, 1998, 1999 by Leon de Boer   }
{   ldeboer@attglobal.net  - primary e-mail addr           }
{   ldeboer@starwon.com.au - backup e-mail addr            }
{                                                          }
{****************[ THIS CODE IS FREEWARE ]*****************}
{                                                          }
{     This sourcecode is released for the purpose to       }
{   promote the pascal language on all platforms. You may  }
{   redistribute it and/or modify with the following       }
{   DISCLAIMER.                                            }
{                                                          }
{     This SOURCE CODE is distributed "AS IS" WITHOUT      }
{   WARRANTIES AS TO PERFORMANCE OF MERCHANTABILITY OR     }
{   ANY OTHER WARRANTIES WHETHER EXPRESSED OR IMPLIED.     }
{                                                          }
{*****************[ SUPPORTED PLATFORMS ]******************}
{     16 and 32 Bit compilers                              }
{        DOS      - Turbo Pascal 7.0 +      (16 Bit)       }
{        DPMI     - Turbo Pascal 7.0 +      (16 Bit)       }
{                 - FPC 0.9912+ (GO32V2)    (32 Bit)       }
{        WINDOWS  - Turbo Pascal 7.0 +      (16 Bit)       }
{                 - Delphi 1.0+             (16 Bit)       }
{        WIN95/NT - Delphi 2.0+             (32 Bit)       }
{                 - Virtual Pascal 2.0+     (32 Bit)       }
{                 - Speedsoft Sybil 2.0+    (32 Bit)       }
{                 - FPC 0.9912+             (32 Bit)       }
{        OS2      - Virtual Pascal 1.0+     (32 Bit)       }
{                 - Speed pascal 1.0+       (32 Bit)       }
{                                                          }
{******************[ REVISION HISTORY ]********************}
{  Version  Date        Fix                                }
{  -------  ---------   ---------------------------------  }
{  1.00     12 Jun 96   Initial DOS/DPMI code released.    }
{  1.10     18 Oct 97   Code converted to GUI & TEXT mode. }
{  1.20     18 Jul 97   Windows conversion added.          }
{  1.30     29 Aug 97   Platform.inc sort added.           }
{  1.40     22 Oct 97   Delphi3 32 bit code added.         }
{  1.50     05 May 98   Virtual pascal 2.0 code added.     }
{  1.60     11 May 98   Clean up and ensure error checks.  }
{  1.70     15 May 98   Documentation & format completed.  }
{  1.80     30 Sep 99   Complete recheck preformed         }
{  1.81     03 Nov 99   FPC windows support added          }
{  1.90     26 Nov 99   Graphics stuff moved to GFVGraph   }
{**********************************************************}

UNIT Menus;

{<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
                                  INTERFACE
{<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}

{====Include file to sort compiler platform out =====================}
{$I Platform.inc}
{====================================================================}

{==== Compiler directives ===========================================}

{$IFNDEF PPC_FPC}{ FPC doesn't support these switches }
  {$F-} { Near calls are okay }
  {$A+} { Word Align Data }
  {$B-} { Allow short circuit boolean evaluations }
  {$O+} { This unit may be overlaid }
  {$G+} { 286 Code optimization - if you're on an 8088 get a real computer }
  {$P-} { Normal string variables }
  {$N-} { No 80x87 code generation }
  {$E+} { Emulation is on }
{$ENDIF}

{$X+} { Extended syntax is ok }
{$R-} { Disable range checking }
{$S-} { Disable Stack Checking }
{$I-} { Disable IO Checking }
{$Q-} { Disable Overflow Checking }
{$V-} { Turn off strict VAR strings }
{====================================================================}

USES
   {$IFDEF OS_WINDOWS}                                { WIN/NT CODE }
     {$IFNDEF PPC_SPEED}                              { NON SPEED COMPILER }
       {$IFDEF PPC_FPC}                               { FPC WINDOWS COMPILER }
       Windows,                                       { Standard unit }
       {$ELSE}                                        { OTHER COMPILERS }
       WinTypes,WinProcs,                             { Standard units }
       {$ENDIF}
     {$ELSE}                                          { SPEEDSOFT COMPILER }
       WinBase, WinDef,                               { Standard units }
     {$ENDIF}
   {$ENDIF}

   GFVGraph,                                          { GFV standard unit }
   Objects, Drivers, Views;                           { GFV standard units }

{***************************************************************************}
{                              PUBLIC CONSTANTS                             }
{***************************************************************************}

{---------------------------------------------------------------------------}
{                               COLOUR PALETTES                             }
{---------------------------------------------------------------------------}
CONST
   CMenuView   = #2#3#4#5#6#7;                        { Menu colours }
   CStatusLine = #2#3#4#5#6#7;                        { Statusline colours }

{***************************************************************************}
{                            RECORD DEFINITIONS                             }
{***************************************************************************}
TYPE
   TMenuStr = String[31];                             { Menu string }

   PMenu = ^TMenu;                                    { Pointer to menu }

{---------------------------------------------------------------------------}
{                              TMenuItem RECORD                             }
{---------------------------------------------------------------------------}
   PMenuItem = ^TMenuItem;
   TMenuItem = PACKED RECORD
     Next: PMenuItem;                                 { Next menu item }
     Name: PString;                                   { Menu item name }
     Command: Word;                                   { Menu item command }
     Disabled: Boolean;                               { Menu item state }
     KeyCode: Word;                                   { Menu item keycode }
     HelpCtx: Word;                                   { Menu item help ctx }
     Case Integer Of
       0: (Param: PString);
       1: (SubMenu: PMenu);
   END;

{---------------------------------------------------------------------------}
{                                TMenu RECORD                               }
{---------------------------------------------------------------------------}
   TMenu = PACKED RECORD
     Items: PMenuItem;                                { Menu item list }
     Default: PMenuItem;                              { Default menu }
   END;

{---------------------------------------------------------------------------}
{                             TStatusItem RECORD                            }
{---------------------------------------------------------------------------}
TYPE
   PStatusItem = ^TStatusItem;
   TStatusItem = PACKED RECORD
     Next: PStatusItem;                               { Next status item }
     Text: PString;                                   { Text of status item }
     KeyCode: Word;                                   { Keycode of item }
     Command: Word;                                   { Command of item }
   END;

{---------------------------------------------------------------------------}
{                             TStatusDef RECORD                             }
{---------------------------------------------------------------------------}
TYPE
   PStatusDef = ^TStatusDef;
   TStatusDef = PACKED RECORD
     Next: PStatusDef;                                { Next status defined }
     Min, Max: Word;                                  { Range of item }
     Items: PStatusItem;                              { Item list }
   END;

{***************************************************************************}
{                            OBJECT DEFINITIONS                             }
{***************************************************************************}

{---------------------------------------------------------------------------}
{                TMenuView OBJECT - MENU VIEW ANCESTOR OBJECT               }
{---------------------------------------------------------------------------}
TYPE
   PMenuView = ^TMenuView;
   TMenuView = OBJECT (TView)
         ParentMenu: PMenuView;                       { Parent menu }
         Menu      : PMenu;                           { Menu item list }
         Current   : PMenuItem;                       { Current menu item }
         OldItem   : PMenuItem;                       { Old item for draws }
      CONSTRUCTOR Init (Var Bounds: TRect);
      CONSTRUCTOR Load (Var S: TStream);
      FUNCTION Execute: Word; Virtual;
      FUNCTION GetHelpCtx: Word; Virtual;
      FUNCTION GetPalette: PPalette; Virtual;
      FUNCTION FindItem (Ch: Char): PMenuItem;
      FUNCTION HotKey (KeyCode: Word): PMenuItem;
      FUNCTION NewSubView (Var Bounds: TRect; AMenu: PMenu;
        AParentMenu: PMenuView): PMenuView; Virtual;
      PROCEDURE Store (Var S: TStream);
      PROCEDURE HandleEvent (Var Event: TEvent); Virtual;
      PROCEDURE GetItemRect (Item: PMenuItem; Var R: TRect); Virtual;
      private
      PROCEDURE GetItemRectX (Item: PMenuItem; Var R: TRect); Virtual;
   END;

{---------------------------------------------------------------------------}
{                    TMenuBar OBJECT - MENU BAR OBJECT                      }
{---------------------------------------------------------------------------}
TYPE
   TMenuBar = OBJECT (TMenuView)
      CONSTRUCTOR Init (Var Bounds: TRect; AMenu: PMenu);
      DESTRUCTOR Done; Virtual;
      PROCEDURE Draw; Virtual;
      PROCEDURE DrawBackGround; Virtual;
      private
      PROCEDURE GetItemRectX (Item: PMenuItem; Var R: TRect); Virtual;
   END;
   PMenuBar = ^TMenuBar;

{---------------------------------------------------------------------------}
{                   TMenuBox OBJECT - BOXED MENU OBJECT                     }
{---------------------------------------------------------------------------}
TYPE
   TMenuBox = OBJECT (TMenuView)
      CONSTRUCTOR Init (Var Bounds: TRect; AMenu: PMenu;
        AParentMenu: PMenuView);
      PROCEDURE Draw; Virtual;
      PROCEDURE DrawBackGround; Virtual;
      private
      PROCEDURE GetItemRectX (Item: PMenuItem; Var R: TRect); Virtual;
   END;
   PMenuBox = ^TMenuBox;

{---------------------------------------------------------------------------}
{                  TMenuPopUp OBJECT - POPUP MENU OBJECT                    }
{---------------------------------------------------------------------------}
TYPE
   TMenuPopup = OBJECT (TMenuBox)
      CONSTRUCTOR Init (Var Bounds: TRect; AMenu: PMenu);
      DESTRUCTOR Done; Virtual;
      PROCEDURE HandleEvent (Var Event: TEvent); Virtual;
   END;
   PMenuPopup = ^TMenuPopup;

{---------------------------------------------------------------------------}
{                    TStatusLine OBJECT - STATUS LINE OBJECT                }
{---------------------------------------------------------------------------}
TYPE
   TStatusLine = OBJECT (TView)
         Items: PStatusItem;                          { Status line items }
         Defs : PStatusDef;                           { Status line default }
      CONSTRUCTOR Init (Var Bounds: TRect; ADefs: PStatusDef);
      CONSTRUCTOR Load (Var S: TStream);
      DESTRUCTOR Done; Virtual;
      FUNCTION GetPalette: PPalette; Virtual;
      FUNCTION Hint (AHelpCtx: Word): String; Virtual;
      PROCEDURE Draw; Virtual;
      PROCEDURE Update; Virtual;
      PROCEDURE Store (Var S: TStream);
      PROCEDURE HandleEvent (Var Event: TEvent); Virtual;
      PRIVATE
      PROCEDURE FindItems;
      PROCEDURE DrawSelect (Selected: PStatusItem);
   END;
   PStatusLine = ^TStatusLine;

{***************************************************************************}
{                            INTERFACE ROUTINES                             }
{***************************************************************************}

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                           MENU INTERFACE ROUTINES                         }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{-NewMenu------------------------------------------------------------
Allocates and returns a pointer to a new TMenu record. Sets the Items
and Default fields of the record to the value given by the parameter.
An error creating will return a nil pointer.
14May98 LdB
---------------------------------------------------------------------}
FUNCTION NewMenu (Items: PMenuItem): PMenu;

{-DisposeMenu--------------------------------------------------------
Disposes of all the elements of the specified menu (and all submenus).
14May98 LdB
---------------------------------------------------------------------}
PROCEDURE DisposeMenu (Menu: PMenu);

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                             MENU ITEM ROUTINES                            }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{-NewLine------------------------------------------------------------
Allocates and returns a pointer to a new TMenuItem record that
represents a separator line in a menu box.
An error creating will return a nil pointer.
14May98 LdB
---------------------------------------------------------------------}
FUNCTION NewLine (Next: PMenuItem): PMenuItem;

{-NewItem------------------------------------------------------------
Allocates and returns a pointer to a new TMenuItem record that
represents a menu item (using NewStr to allocate the Name and Param).
An error creating will return a nil pointer.
14May98 LdB
---------------------------------------------------------------------}
FUNCTION NewItem (Name, Param: TMenuStr; KeyCode: Word; Command: Word;
  AHelpCtx: Word; Next: PMenuItem): PMenuItem;

{-NewSubMenu---------------------------------------------------------
Allocates and returns a pointer to a new TMenuItem record, which
represents a submenu (using NewStr to allocate the Name).
An error creating will return a nil pointer.
14May98 LdB
---------------------------------------------------------------------}
FUNCTION NewSubMenu (Name: TMenuStr; AHelpCtx: Word; SubMenu: PMenu;
  Next: PMenuItem): PMenuItem;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                          STATUS INTERFACE ROUTINES                        }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{-NewStatusDef-------------------------------------------------------
Allocates and returns a pointer to a new TStatusDef record initialized
with the given parameter values. Calls to NewStatusDef can be nested.
An error creating will return a nil pointer.
15May98 LdB
---------------------------------------------------------------------}
FUNCTION NewStatusDef (AMin, AMax: Word; AItems: PStatusItem;
  ANext: PStatusDef): PStatusDef;

{-NewStatusKey-------------------------------------------------------
Allocates and returns a pointer to a new TStatusItem record initialized
with the given parameter values (using NewStr to allocate the Text).
An error in creating will return a nil pointer.
15May98 LdB
---------------------------------------------------------------------}
FUNCTION NewStatusKey (AText: String; AKeyCode: Word; ACommand: Word;
  ANext: PStatusItem): PStatusItem;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                           OBJECT REGISTER ROUTINES                        }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{-RegisterMenus-------------------------------------------------------
Calls RegisterType for each of the object types defined in this unit.
15May98 LdB
---------------------------------------------------------------------}
PROCEDURE RegisterMenus;

{***************************************************************************}
{                           OBJECT REGISTRATION                             }
{***************************************************************************}

{---------------------------------------------------------------------------}
{                        TMenuBar STREAM REGISTRATION                       }
{---------------------------------------------------------------------------}
CONST
   RMenuBar: TStreamRec = (
     ObjType: 40;                                     { Register id = 40 }
     {$IFDEF BP_VMTLink}                              { BP style VMT link }
     VmtLink: Ofs(TypeOf(TMenuBar)^);
     {$ELSE}                                          { Alt style VMT link }
     VmtLink: TypeOf(TMenuBar);
     {$ENDIF}
     Load:    @TMenuBar.Load;                         { Object load method }
     Store:   @TMenuBar.Store                         { Object store method }
   );

{---------------------------------------------------------------------------}
{                        TMenuBox STREAM REGISTRATION                       }
{---------------------------------------------------------------------------}
CONST
   RMenuBox: TStreamRec = (
     ObjType: 41;                                     { Register id = 41 }
     {$IFDEF BP_VMTLink}                              { BP style VMT link }
     VmtLink: Ofs(TypeOf(TMenuBox)^);
     {$ELSE}                                          { Alt style VMT link }
     VmtLink: TypeOf(TMenuBox);
     {$ENDIF}
     Load:    @TMenuBox.Load;                         { Object load method }
     Store:   @TMenuBox.Store                         { Object store method }
   );

{---------------------------------------------------------------------------}
{                      TStatusLine STREAM REGISTRATION                      }
{---------------------------------------------------------------------------}
CONST
   RStatusLine: TStreamRec = (
     ObjType: 42;                                     { Register id = 42 }
     {$IFDEF BP_VMTLink}                              { BP style VMT link }
     VmtLink: Ofs(TypeOf(TStatusLine)^);
     {$ELSE}                                          { Alt style VMT link }
     VmtLink: TypeOf(TStatusLine);
     {$ENDIF}
     Load:    @TStatusLine.Load;                      { Object load method }
     Store:   @TStatusLine.Store                      { Object store method }
   );

{---------------------------------------------------------------------------}
{                       TMenuPopup STREAM REGISTRATION                      }
{---------------------------------------------------------------------------}
CONST
   RMenuPopup: TStreamRec = (
     ObjType: 43;                                     { Register id = 43 }
     {$IFDEF BP_VMTLink}                              { BP style VMT link }
     VmtLink: Ofs(TypeOf(TMenuPopup)^);
     {$ELSE}                                          { Alt style VMT link }
     VmtLink: TypeOf(TMenuPopup);
     {$ENDIF}
     Load:    @TMenuPopup.Load;                       { Object load method }
     Store:   @TMenuPopup.Store                       { Object store method }
   );

{***************************************************************************}
{                        INITIALIZED PUBLIC VARIABLES                       }
{***************************************************************************}

{---------------------------------------------------------------------------}
{                       INITIALIZED PUBLIC VARIABLES                        }
{---------------------------------------------------------------------------}
CONST
   AdvancedMenus: Boolean = False;                    { Advanced menus }

{<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
                                IMPLEMENTATION
{<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
{$ifndef GRAPH_API}
USES
  Video;
{$endif not GRAPH_API}

CONST
  SubMenuChar : array[boolean] of char = ('>',#16);
  { SubMenuChar is the character displayed at right of submenu }

{***************************************************************************}
{                               OBJECT METHODS                              }
{***************************************************************************}

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                          TMenuView OBJECT METHODS                         }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{--TMenuView----------------------------------------------------------------}
{  Init -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 08May98 LdB              }
{---------------------------------------------------------------------------}
CONSTRUCTOR TMenuView.Init (Var Bounds: TRect);
BEGIN
   Inherited Init(Bounds);                            { Call ancestor }
   EventMask := EventMask OR evBroadcast;             { See broadcast events }
END;

{--TMenuView----------------------------------------------------------------}
{  Load -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 30Sep99 LdB              }
{---------------------------------------------------------------------------}
CONSTRUCTOR TMenuView.Load (Var S: TStream);

   FUNCTION DoLoadMenu: PMenu;
   VAR Tok: Byte; Item: PMenuItem; Last: ^PMenuItem; Menu: PMenu;
   BEGIN
     New(Menu);                                       { Create new menu }
     Last := @Menu^.Items;                            { Start on first item }
     Item := Nil;                                     { Clear pointer }
     S.Read(Tok, SizeOf(Tok));                        { Read token }
     While (Tok <> 0) Do Begin
       New(Item);                                     { Create new item }
       Last^ := Item;                                 { First part of chain }
       If (Item <> Nil) Then Begin                    { Check item valid }
         Last := @Item^.Next;                         { Complete chain }
         With Item^ Do Begin
           Name := S.ReadStr;                         { Read menu name }
           S.Read(Command, SizeOf(Command));          { Menu item command }
           S.Read(Disabled, SizeOf(Disabled));        { Menu item state }
           S.Read(KeyCode, SizeOf(KeyCode));          { Menu item keycode }
           S.Read(HelpCtx, SizeOf(HelpCtx));          { Menu item help ctx }
           If (Name <> Nil) Then
             If Command = 0 Then
{$ifdef PPC_FPC}
               SubMenu := DoLoadMenu()                  { Load submenu }
{$else not PPC_FPC}
               SubMenu := DoLoadMenu                  { Load submenu }
{$endif not PPC_FPC}
                 Else Param := S.ReadStr;             { Read param string }
         End;
       End;
       S.Read(Tok, SizeOf(Tok));                      { Read token }
     End;
     Last^ := Nil;                                    { List complete }
     Menu^.Default := Menu^.Items;                    { Set menu default }
     DoLoadMenu := Menu;                              { Return menu }
   End;

BEGIN
   Inherited Load(S);                                 { Call ancestor }
   Menu := DoLoadMenu;                                { Load menu items }
END;

{--TMenuView----------------------------------------------------------------}
{  Execute -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 30Sep99 LdB           }
{---------------------------------------------------------------------------}
FUNCTION TMenuView.Execute: Word;
TYPE MenuAction = (DoNothing, DoSelect, DoReturn);
VAR AutoSelect: Boolean; Action: MenuAction; Ch: Char; Res: Word; R: TRect;
  ItemShown, P: PMenuItem; Target: PMenuView; E: TEvent; MouseActive: Boolean;

   PROCEDURE TrackMouse;
   VAR Mouse: TPoint; R: TRect;
   BEGIN
     Mouse.X := E.Where.X - RawOrigin.X;              { Local x position }
     Mouse.Y := E.Where.Y - RawoRigin.Y;              { Local y position }
     Current := Menu^.Items;                          { Start with current }
     While (Current <> Nil) Do Begin
       GetItemRectX(Current, R);                       { Get item rectangle }
       If R.Contains(Mouse) Then Begin                { Contains mouse }
         MouseActive := True;                         { Return true }
         Exit;                                        { Then exit }
       End;
       Current := Current^.Next;                      { Try next item }
     End;
   END;

   PROCEDURE TrackKey (FindNext: Boolean);

       PROCEDURE NextItem;
       BEGIN
         Current := Current^.Next;                    { Move to next item }
         If (Current = Nil) Then
           Current := Menu^.Items;                    { Return first menu }
       END;

       PROCEDURE PrevItem;
       VAR P: PMenuItem;
       BEGIN
         P := Current;                                { Start on current }
         If (P = Menu^.Items) Then P := Nil;          { Check if at start }
         Repeat NextItem Until Current^.Next = P;     { Prev item found }
       END;

   BEGIN
     If (Current <> Nil) Then                         { Current view valid }
       Repeat
         If FindNext Then NextItem Else PrevItem;     { Find next/prev item }
       Until (Current^.Name <> Nil);                  { Until we have name }
   END;

   FUNCTION MouseInOwner: Boolean;
   VAR Mouse: TPoint; R: TRect;
   BEGIN
     MouseInOwner := False;                           { Preset false }
     If (ParentMenu <> Nil) AND (ParentMenu^.Size.Y = 1)
     Then Begin                                       { Valid parent menu }
       Mouse.X := E.Where.X - ParentMenu^.RawOrigin.X;{ Local x position }
       Mouse.Y := E.Where.Y - ParentMenu^.RawOrigin.Y;{ Local y position }
       ParentMenu^.GetItemRectX(ParentMenu^.Current,R);{ Get item rect }
       MouseInOwner := R.Contains(Mouse);             { Return result }
     End;
   END;

   FUNCTION MouseInMenus: Boolean;
   VAR P: PMenuView;
   BEGIN
     P := ParentMenu;                                 { Parent menu }
     While (P <> Nil) AND NOT P^.MouseInView(E.Where)
       Do P := P^.ParentMenu;                         { Check next menu }
     MouseInMenus := (P <> Nil);                      { Return result }
   END;

   FUNCTION TopMenu: PMenuView;
   VAR P: PMenuView;
   BEGIN
     P := @Self;                                      { Start with self }
     While (P^.ParentMenu <> Nil) Do
       P := P^.ParentMenu;                            { Check next menu }
     TopMenu := P;                                    { Top menu }
   END;

BEGIN
   AutoSelect := False;                               { Clear select flag }
   MouseActive := False;                              { Clear mouse flag }
   Res := 0;                                          { Clear result }
   ItemShown := Nil;                                  { Clear item pointer }
   If (Menu <> Nil) Then Current := Menu^.Default     { Set current item }
     Else Current := Nil;                             { No menu = no current }
   Repeat
     Action := DoNothing;                             { Clear action flag }
     GetEvent(E);                                     { Get next event }
     Case E.What Of
       evMouseDown: If MouseInView(E.Where)           { Mouse in us }
         OR MouseInOwner Then Begin                   { Mouse in owner area }
           TrackMouse;                                { Track the mouse }
           If (Size.Y = 1) Then AutoSelect := True;   { Set select flag }
         End Else Action := DoReturn;                 { Set return action }
       evMouseUp: Begin
           TrackMouse;                                { Track the mouse }
           If MouseInOwner Then                       { Mouse in owner }
             Current := Menu^.Default                 { Set as current }
           Else If (Current <> Nil) AND
           (Current^.Name <> Nil) Then
             Action := DoSelect                       { Set select action }
           Else If MouseActive OR MouseInView(E.Where)
           Then Action := DoReturn                    { Set return action }
           Else Begin
             Current := Menu^.Default;                { Set current item }
             If (Current = Nil) Then
               Current := Menu^.Items;                { Select first item }
             Action := DoNothing;                     { Do nothing action }
           End;
         End;
       evMouseMove: If (E.Buttons <> 0) Then Begin    { Mouse moved }
           TrackMouse;                                { Track the mouse }
           If NOT (MouseInView(E.Where) OR MouseInOwner)
           AND MouseInMenus Then Action := DoReturn;  { Set return action }
         End;
       evKeyDown:
         Case CtrlToArrow(E.KeyCode) Of               { Check arrow keys }
           kbUp, kbDown: If (Size.Y <> 1) Then
             TrackKey(CtrlToArrow(E.KeyCode) = kbDown){ Track keyboard }
             Else If (E.KeyCode = kbDown) Then        { Down arrow }
             AutoSelect := True;                      { Select item }
           kbLeft, kbRight: If (ParentMenu = Nil) Then
             TrackKey(CtrlToArrow(E.KeyCode)=kbRight) { Track keyboard }
             Else Action := DoReturn;                 { Set return action }
           kbHome, kbEnd: If (Size.Y <> 1) Then Begin
               Current := Menu^.Items;                { Set to first item }
               If (E.KeyCode = kbEnd) Then            { If the 'end' key }
                 TrackKey(False);                     { Move to last item }
             End;
           kbEnter: Begin
               If Size.Y = 1 Then AutoSelect := True; { Select item }
               Action := DoSelect;                    { Return the item }
             End;
           kbEsc: Begin
               Action := DoReturn;                    { Set return action }
               If (ParentMenu = Nil) OR
               (ParentMenu^.Size.Y <> 1) Then         { Check parent }
                 ClearEvent(E);                       { Kill the event }
             End;
           Else Target := @Self;                      { Set target as self }
           Ch := GetAltChar(E.KeyCode);
           If (Ch = #0) Then Ch := E.CharCode Else
             Target := TopMenu;                       { Target is top menu }
           P := Target^.FindItem(Ch);                 { Check for item }
           If (P = Nil) Then Begin
             P := TopMenu^.HotKey(E.KeyCode);         { Check for hot key }
             If (P <> Nil) AND                        { Item valid }
             CommandEnabled(P^.Command) Then Begin    { Command enabled }
               Res := P^.Command;                     { Set return command }
               Action := DoReturn;                    { Set return action }
             End
           End Else If Target = @Self Then Begin
             If Size.Y = 1 Then AutoSelect := True;   { Set auto select }
             Action := DoSelect;                      { Select item }
             Current := P;                            { Set current item }
           End Else If (ParentMenu <> Target) OR
           (ParentMenu^.Current <> P) Then            { Item different }
              Action := DoReturn;                     { Set return action }
         End;
       evCommand: If (E.Command = cmMenu) Then Begin  { Menu command }
           AutoSelect := False;                       { Dont select item }
           If (ParentMenu <> Nil) Then
             Action := DoReturn;                      { Set return action }
         End Else Action := DoReturn;                 { Set return action }
     End;
     If (ItemShown <> Current) Then Begin             { New current item }
       OldItem := ItemShown;                          { Hold old item }
       ItemShown := Current;                          { Hold new item }
       SetDrawMask(vdFocus OR vdInner);               { Set the draw mask }
       DrawView;                                      { Redraw the items }
       OldItem := Nil;                                { Clear old item }
     End;
     If (Action = DoSelect) OR ((Action = DoNothing)
     AND AutoSelect) Then                             { Item is selecting }
       If (Current <> Nil) Then With Current^ Do      { Current item valid }
         If (Name <> Nil) Then                        { Item has a name }
           If (Command = 0) Then Begin                { Has no command }
             If (E.What AND (evMouseDown+evMouseMove) <> 0)
               Then PutEvent(E);                      { Put event on queue }
             GetItemRectX(Current, R);                 { Get area of item }
             R.A.X := R.A.X DIV FontWidth + Origin.X; { Left start point }
             R.A.Y := R.B.Y DIV FontHeight + Origin.Y;{ Top start point }
             R.B.X := Owner^.Size.X;                  { X screen area left }
             R.B.Y := Owner^.Size.Y;                  { Y screen area left }
             Target := TopMenu^.NewSubView(R, SubMenu,
               @Self);                                { Create drop menu }
             Res := Owner^.ExecView(Target);          { Execute dropped view }
             Dispose(Target, Done);                   { Dispose drop view }
           End Else If Action = DoSelect Then
             Res := Command;                          { Return result }
     If (Res <> 0) AND CommandEnabled(Res)            { Check command }
     Then Begin
       Action := DoReturn;                            { Return command }
       ClearEvent(E);                                 { Clear the event }
     End Else Res := 0;                               { Clear result }
   Until (Action = DoReturn);
   If (E.What <> evNothing) Then
     If (ParentMenu <> Nil) OR (E.What = evCommand)   { Check event type }
       Then PutEvent(E);                              { Put event on queue }
   If (Current <> Nil) Then Begin
     Menu^.Default := Current;                        { Set new default }
     Current := Nil;                                  { Clear current }
     DrawView;                                        { Redraw the view }
   End;
   Execute := Res;                                    { Return result }
END;

{--TMenuView----------------------------------------------------------------}
{  GetHelpCtx -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 08May98 LdB        }
{---------------------------------------------------------------------------}
FUNCTION TMenuView.GetHelpCtx: Word;
VAR C: PMenuView;
BEGIN
   C := @Self;                                        { Start at self }
   While (C <> Nil) AND ((C^.Current = Nil) OR
   (C^.Current^.HelpCtx = hcNoContext) OR             { Has no context }
   (C^.Current^.Name = Nil)) Do C := C^.ParentMenu;   { Parent menu context }
   If (C<>Nil) Then GetHelpCtx := C^.Current^.HelpCtx { Current context }
     Else GetHelpCtx := hcNoContext;                  { No help context }
END;

{--TMenuView----------------------------------------------------------------}
{  GetPalette -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 15May98 LdB        }
{---------------------------------------------------------------------------}
FUNCTION TMenuView.GetPalette: PPalette;
{$IFDEF PPC_DELPHI3}                                  { DELPHI3+ COMPILER }
CONST P: String = CMenuView;                          { Possible huge string }
{$ELSE}                                               { OTHER COMPILERS }
CONST P: String[Length(CMenuView)] = CMenuView;       { Always normal string }
{$ENDIF}
BEGIN
   GetPalette := @P;                                  { Return palette }
END;

{--TMenuView----------------------------------------------------------------}
{  FindItem -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB          }
{---------------------------------------------------------------------------}
FUNCTION TMenuView.FindItem (Ch: Char): PMenuItem;
VAR I: Integer; P: PMenuItem;
BEGIN
   Ch := UpCase(Ch);                                  { Upper case of char }
   P := Menu^.Items;                                  { First menu item }
   While (P <> Nil) Do Begin                          { While item valid }
     If (P^.Name <> Nil) AND (NOT P^.Disabled)        { Valid enabled cmd }
     Then Begin
       I := Pos('~', P^.Name^);                       { Scan for highlight }
       If (I <> 0) AND (Ch = UpCase(P^.Name^[I+1]))   { Hotkey char found }
       Then Begin
         FindItem := P;                               { Return item }
         Exit;                                        { Now exit }
       End;
     End;
     P := P^.Next;                                    { Next item }
   End;
   FindItem := Nil;                                   { No item found }
END;

{--TMenuView----------------------------------------------------------------}
{  HotKey -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB            }
{---------------------------------------------------------------------------}
FUNCTION TMenuView.HotKey (KeyCode: Word): PMenuItem;

   FUNCTION FindHotKey (P: PMenuItem): PMenuItem;
   VAR T: PMenuItem;
   BEGIN
     While (P <> Nil) Do Begin                        { While item valid }
       If (P^.Name <> Nil) Then                       { If valid name }
         If (P^.Command = 0) Then Begin               { Valid command }
           T := FindHotKey(P^.SubMenu^.Items);        { Search for hot key }
           If (T <> Nil) Then Begin
             FindHotKey := T;                         { Return hotkey }
             Exit;                                    { Now exit }
           End;
         End Else If NOT P^.Disabled AND              { Hotkey is enabled }
         (P^.KeyCode <> kbNoKey) AND                  { Valid keycode }
         (P^.KeyCode = KeyCode) Then Begin            { Key matches request }
           FindHotKey := P;                           { Return hotkey code }
           Exit;                                      { Exit }
         End;
         P := P^.Next;                                { Next item }
     End;
     FindHotKey := Nil;                               { No item found }
   END;

BEGIN
   HotKey := FindHotKey(Menu^.Items);                 { Hot key function }
END;

{--TMenuView----------------------------------------------------------------}
{  NewSubView -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB        }
{---------------------------------------------------------------------------}
FUNCTION TMenuView.NewSubView (Var Bounds: TRect; AMenu: PMenu;
  AParentMenu: PMenuView): PMenuView;
BEGIN
   NewSubView := New(PMenuBox, Init(Bounds, AMenu,
     AParentMenu));                                   { Create a menu box }
END;

{--TMenuView----------------------------------------------------------------}
{  Store -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB             }
{---------------------------------------------------------------------------}
PROCEDURE TMenuView.Store (Var S: TStream);

   PROCEDURE DoStoreMenu (Menu: PMenu);
   VAR Item: PMenuItem; Tok: Byte;
   BEGIN
     Tok := $FF;                                      { Preset max count }
     Item := Menu^.Items;                             { Start first item }
     While (Item <> Nil) Do Begin
       With Item^ Do Begin
         S.Write(Tok, SizeOf(Tok));                      { Write tok value }
         S.WriteStr(Name);                            { Write item name }
         S.Write(Command, SizeOf(Command));           { Menu item command }
         S.Write(Disabled, SizeOf(Disabled));         { Menu item state }
         S.Write(KeyCode, SizeOf(KeyCode));           { Menu item keycode }
         S.Write(HelpCtx, SizeOf(HelpCtx));           { Menu item help ctx }
         If (Name <> Nil) Then
           If Command = 0 Then DoStoreMenu(SubMenu)
           Else S.WriteStr(Param);                    { Write parameter }
       End;
       Item := Item^.Next;                            { Next item }
     End;
     Tok := 0;                                        { Clear tok count }
     S.Write(Tok, SizeOf(Tok));                       { Write tok value }
   END;

BEGIN
   TView.Store(S);                                    { TView.Store called }
   DoStoreMenu(Menu);                                 { Store menu items }
END;

{--TMenuView----------------------------------------------------------------}
{  HandleEvent -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 08May98 LdB       }
{---------------------------------------------------------------------------}
PROCEDURE TMenuView.HandleEvent (Var Event: TEvent);
VAR CallDraw: Boolean; P: PMenuItem;

   PROCEDURE UpdateMenu (Menu: PMenu);
   VAR P: PMenuItem; CommandState: Boolean;
   BEGIN
     P := Menu^.Items;                                { Start on first item }
     While (P <> Nil) Do Begin
       If (P^.Name <> Nil) Then                       { Valid name }
       If (P^.Command = 0) Then UpdateMenu(P^.SubMenu){ Update menu }
       Else Begin
         CommandState := CommandEnabled(P^.Command);  { Menu item state }
         If (P^.Disabled = CommandState) Then Begin
           P^.Disabled := NOT CommandState;           { Disable item }
           CallDraw := True;                          { Must draw }
         End;
       End;
       P := P^.Next;                                  { Next item }
     End;
   END;

   PROCEDURE DoSelect;
   BEGIN
     PutEvent(Event);                                 { Put event on queue }
     Event.Command := Owner^.ExecView(@Self);         { Execute view }
     If (Event.Command <> 0) AND
     CommandEnabled(Event.Command) Then Begin
       Event.What := evCommand;                       { Command event }
       Event.InfoPtr := Nil;                          { Clear info ptr }
       PutEvent(Event);                               { Put event on queue }
     End;
     ClearEvent(Event);                               { Clear the event }
   END;

BEGIN
   If (Menu <> Nil) Then
     Case Event.What Of
       evMouseDown: DoSelect;                         { Select menu item }
       evKeyDown:
         If (FindItem(GetAltChar(Event.KeyCode)) <> Nil)
         Then DoSelect Else Begin                     { Select menu item }
           P := HotKey(Event.KeyCode);                { Check for hotkey }
           If (P <> Nil) AND
           (CommandEnabled(P^.Command)) Then Begin
             Event.What := evCommand;                 { Command event }
             Event.Command := P^.Command;             { Set command event }
             Event.InfoPtr := Nil;                    { Clear info ptr }
             PutEvent(Event);                         { Put event on queue }
             ClearEvent(Event);                       { Clear the event }
           End;
         End;
       evCommand:
         If Event.Command = cmMenu Then DoSelect;     { Select menu item }
       evBroadcast:
         If (Event.Command = cmCommandSetChanged)     { Commands changed }
         Then Begin
           CallDraw := False;                         { Preset no redraw }
           UpdateMenu(Menu);                          { Update menu }
           If CallDraw Then DrawView;                 { Redraw if needed }
         End;
     End;
END;

{--TMenuView----------------------------------------------------------------}
{  GetItemRectX -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 08May98 LdB       }
{---------------------------------------------------------------------------}
PROCEDURE TMenuView.GetItemRectX (Item: PMenuItem; Var R: TRect);
BEGIN                                                 { Abstract method }
END;

{--TMenuView----------------------------------------------------------------}
{  GetItemRect -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 08May98 LdB       }
{---------------------------------------------------------------------------}
PROCEDURE TMenuView.GetItemRect (Item: PMenuItem; Var R: TRect);
BEGIN
  GetItemRectX(Item,R);
  R.A.X:=R.A.X div SysFontWidth;
  R.A.Y:=R.A.Y div SysFontHeight;
  R.B.X:=R.B.X div SysFontWidth;
  R.B.Y:=R.B.Y div SysFontHeight;
END;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                        TMenuBar OBJECT METHODS                            }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{--TMenuBar-----------------------------------------------------------------}
{  Init -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 08May98 LdB              }
{---------------------------------------------------------------------------}
CONSTRUCTOR TMenuBar.Init (Var Bounds: TRect; AMenu: PMenu);
BEGIN
   Inherited Init(Bounds);                            { Call ancestor }
   GrowMode := gfGrowHiX;                             { Set grow mode }
   Menu := AMenu;                                     { Hold menu item }
   Options := Options OR ofPreProcess;                { Preprocessing view }
END;

{--TMenuBar-----------------------------------------------------------------}
{  Done -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 08May98 LdB              }
{---------------------------------------------------------------------------}
DESTRUCTOR TMenuBar.Done;
BEGIN
   If (Menu <> Nil) Then DisposeMenu(Menu);           { Dispose menu items }
   Inherited Done;                                    { Call ancestor }
END;

{--TMenuBar-----------------------------------------------------------------}
{  Draw -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 08May98 LdB              }
{---------------------------------------------------------------------------}
PROCEDURE TMenuBar.Draw;
VAR I, J, K, L, CNormal, CSelect, CNormDisabled, CSelDisabled, Color: Word;
    P: PMenuItem; B: TDrawBuffer;
BEGIN
   CNormal := GetColor($0301);                        { Normal colour }
   CSelect := GetColor($0604);                        { Select colour }
   CNormDisabled := GetColor($0202);                  { Disabled colour }
   CSelDisabled := GetColor($0505);                   { Select disabled }
   If (Menu <> Nil) Then Begin                        { Valid menu }
     I := 0;                                          { Set start position }
     P := Menu^.Items;                                { First item }
     While (P <> Nil) Do Begin
       If (P^.Name <> Nil) Then Begin                 { Name valid }
         J := CStrLen(P^.Name^) + 2;                  { Name string length }
         If (P = OldItem) OR (P = Current) Then Begin { Matches a state }
           If P^.Disabled Then Begin
             If (P = Current) Then
               Color := CSelDisabled                  { Select disabled }
               Else Color := CNormDisabled            { Normal disabled }
           End Else Begin
             If (P = Current) Then Color := CSelect   { Select colour }
               Else Color := CNormal;                 { Normal colour }
           End;
           MoveCStr(B, ' '+P^.Name^+' ', Color);      { Name to buffer }
           WriteBuf(I, 0, J, 1, B);                   { Write the string }
           K := I*FontWidth;                          { X start position }
           L := K + TextWidth(' '+P^.Name^+' ');      { X end position }
           If AdvancedMenus Then Begin
             GraphLine(K, 0, L, 0, White);            { Redraw top line }
             GraphLine(K, FontHeight-1, L,
               FontHeight-1, DarkGray);               { Redraw lower line }
           End;
         End;
         Inc(I, J);                                   { Advance position }
       End;
       P := P^.Next;                                  { Next item }
     End;
   End;
END;

{--TMenuBar-----------------------------------------------------------------}
{  DrawBackGround -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 08May98 LdB    }
{---------------------------------------------------------------------------}
PROCEDURE TMenuBar.DrawBackGround;
VAR I, J, CNormal, CSelect, CNormDisabled, CSelDisabled, Color: Word;
    P: PMenuItem; B: TDrawBuffer;
BEGIN
   Inherited DrawBackGround;                          { Call ancestor }
   CNormal := GetColor($0301);                        { Normal colour }
   CSelect := GetColor($0604);                        { Select colour }
   CNormDisabled := GetColor($0202);                  { Disabled colour }
   CSelDisabled := GetColor($0505);                   { Select disabled }
   If (Menu <> Nil) Then Begin                        { Valid menu }
     I := 0;                                          { Set start position }
     P := Menu^.Items;                                { First item }
     While (P <> Nil) Do Begin
       If (P^.Name <> Nil) Then Begin                 { Name valid }
         If P^.Disabled Then Begin
           If (P = Current) Then Color := CSelDisabled{ Select disabled }
             Else Color := CNormDisabled              { Normal disabled }
         End Else Begin
           If (P = Current) Then Color := CSelect     { Select colour }
             Else Color := CNormal;                   { Normal colour }
         End;
         J := CStrLen(P^.Name^) + 2;                  { Length of string }
         MoveCStr(B, ' '+P^.Name^+' ', Color);        { Name to buffer }
         WriteBuf(I, 0, J, 1, B);                     { Write the string }
         Inc(I, J);                                   { Advance position }
       End;
       P := P^.Next;                                  { Next item }
     End;
   End;
   If AdvancedMenus Then BiColorRectangle(0, 0,
     RawSize.X, RawSize.Y, White, DarkGray, False);   { Draw 3d effect }
END;

{--TMenuBar-----------------------------------------------------------------}
{  GetItemRectX -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 08May98 LdB      }
{---------------------------------------------------------------------------}
PROCEDURE TMenuBar.GetItemRectX (Item: PMenuItem; Var R: TRect);
VAR I: Integer; P: PMenuItem;
BEGIN
   I := 0;                                            { Preset to zero }
   R.Assign(0, 0, 0, FontHeight);                     { Initial rect size }
   P := Menu^.Items;                                  { First item }
   While (P <> Nil) Do Begin                          { While valid item }
     R.A.X := I*FontWidth;                            { Move area along }
     If (P^.Name <> Nil) Then Begin                   { Valid name }
       R.B.X := R.A.X+TextWidth(' ' + P^.Name^ + ' ');{ Add text width  }
       I := I + CStrLen(P^.Name^) + 2;                { Add item length }
     End Else R.B.X := R.A.X;
     If (P = Item) Then break;                        { Requested item found }
     P := P^.Next;                                    { Next item }
   End;
END;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                          TMenuBox OBJECT METHODS                          }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{--TMenuBox-----------------------------------------------------------------}
{  Init -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB              }
{---------------------------------------------------------------------------}
CONSTRUCTOR TMenuBox.Init (Var Bounds: TRect; AMenu: PMenu;
  AParentMenu: PMenuView);
VAR W, H, L: Integer; S: String; P: PMenuItem; R: TRect;
BEGIN
   W := 0;                                            { Clear initial width }
   H := 2;                                            { Set initial height }
   If (AMenu <> Nil) Then Begin                       { Valid menu }
     P := AMenu^.Items;                               { Start on first item }
     While (P <> Nil) Do Begin                        { If item valid }
       If (P^.Name <> Nil) Then Begin                 { Check for name }
         S := ' ' + P^.Name^ + ' ';                   { Transfer string }
         If (P^.Command <> 0) AND (P^.Param <> Nil)
           Then S := S + ' - ' + P^.Param^;           { Add any parameter }
       End;
       L := TextWidth(S);                             { Width of string }
       If (L > W) Then W := L;                        { Hold maximum }
       Inc(H);                                        { Inc count of items }
       P := P^.Next;                                  { Move to next item }
     End;
   End;
   W := 5 + (W DIV FontWidth);                        { Longest text width }
   R.Copy(Bounds);                                    { Copy the bounds }
   If (R.A.X + W < R.B.X) Then R.B.X := R.A.X + W     { Shorten if possible }
     Else R.A.X := R.B.X - W;                         { Insufficent space }
   R.B.X := R.A.X + W;
   If (R.A.Y + H < R.B.Y) Then R.B.Y := R.A.Y + H     { Shorten if possible }
     Else R.A.Y := R.B.Y - H;                         { Insufficent height }
   Inherited Init(R);                                 { Call ancestor }
   State := State OR sfShadow;                        { Set shadow state }
   Options := Options OR ofPreProcess;                { View pre processes }
   if TextModeGFV then
     Options := Options OR ofFramed;
   Menu := AMenu;                                     { Hold menu }
   ParentMenu := AParentMenu;                         { Hold parent }
END;

{--TMenuBox-----------------------------------------------------------------}
{  Draw -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB              }
{---------------------------------------------------------------------------}
PROCEDURE TMenuBox.Draw;
VAR CNormal, CSelect, CSelectDisabled, CDisabled, Color: Word; Index, Tx, Ty, Y: Integer;
    S: String; P: PMenuItem; B: TDrawBuffer;
Type
   FrameLineType = (UpperLine,NormalLine,SeparationLine,LowerLine);
   FrameLineChars = Array[0..2] of char;
Const
   FrameLines : Array[FrameLineType] of FrameLineChars =
     ('ÚÄ¿','³ ³','ÃÄ´','ÀÄÙ');
  Procedure CreateBorder(LineType : FrameLineType);
  Begin
    MoveChar(B, ' ', CNormal, 1);
    MoveChar(B[1], FrameLines[LineType][0], CNormal, 1);
    MoveChar(B[2], FrameLines[LineType][1], Color, Size.X-4);
    MoveChar(B[Size.X-2], FrameLines[LineType][2], CNormal, 1);
    MoveChar(B[Size.X-1], ' ', CNormal, 1);
  End;


BEGIN
   CNormal := GetColor($0301);                        { Normal colour }
   CSelect := GetColor($0604);                        { Selected colour }
   CDisabled := GetColor($0202);                      { Disabled colour }
   CSelectDisabled := GetColor($0505);                { Selected, but disabled }
   If TextModeGFV then
     Begin
       Color := CNormal;                              { Normal colour }
       CreateBorder(UpperLine);
       WriteBuf(0, 0, Size.X, 1, B);                  { Write the line }
     End;
   Y := 1;
   If (Menu <> Nil) Then Begin                        { We have a menu }
     P := Menu^.Items;                                { Start on first }
     While (P <> Nil) Do Begin                        { Valid menu item }
       Color := CNormal;                              { Normal colour }
       If (P^.Name <> Nil) Then Begin                 { Item has text }
         If P^.Disabled Then
           begin
             if (P = Current) then
               Color := CSelectDisabled
             else
               Color := CDisabled; { Is item disabled }
           end
         else
           If (P = Current) Then Color := CSelect;    { Select colour }
         If TextModeGFV or UseFixedFont then
           Begin
             If Not TextModeGFV then
               MoveChar(B, ' ', Color, Size.X);    { Clear buffer }
             If TextModeGFV  then
               CreateBorder(NormalLine);
             Index:=2;
           End
         Else
           Begin
             MoveChar(B, ' ', Color, Size.X-4);    { Clear buffer }
             Index:=0;
           End;
         S := ' ' + P^.Name^ + ' ';                   { Menu string }
         MoveCStr(B[Index], S, Color);                { Transfer string }
        if P^.Command = 0 then
          MoveChar(B[Size.X - 4],SubMenuChar[LowAscii],
            Byte(Color), 1) else
         If (P^.Command <> 0) AND(P^.Param <> Nil)
         Then Begin
           if TextModeGFV or UseFixedFont then
            MoveCStr(B[Size.X - 3 - Length(P^.Param^)], P^.Param^, Color)  { Add param chars }
           else
            S := S + ' - ' + P^.Param^;                { Add to string }
         End;
         If (OldItem = Nil) OR (OldItem = P) OR
         (Current = P) Then Begin                     { We need to fix draw }
           If TextModeGFV or UseFixedFont then
             Begin
               if TextModeGFV then
                 WriteBuf(0, Y, Size.X, 1, B)             { Write the whole line }
               else
                 WriteBuf(1, Y, Size.X-2, 1, B[1]);
             end
           Else
             WriteBuf(2, Y, CStrLen(S), 1, B);          { Write the line }
           If (P = Current) Then Begin                { Selected item }
             Tx := 2 * FontWidth;                     { X offset }
             Ty := Y * FontHeight;                    { Y offset }
             BicolorRectangle(Tx, Ty, Tx + TextWidth(S)
               - 1, Ty + FontHeight - 1, White,
               DarkGray, False);                      { Draw higlight box }
           End;
         End;
       End Else Begin { no text NewLine }
         Color := CNormal;                              { Normal colour }
         If TextModeGFV then
           Begin
             CreateBorder(SeparationLine);
             WriteBuf(0, Y, Size.X, 1, B);                { Write the line }
           End;
       End;
       Inc(Y);                                        { Next line down }
       P := P^.Next;                                  { fetch next item }
     End;
   End;
   If TextModeGFV then
     Begin
       Color := CNormal;                              { Normal colour }
       CreateBorder(LowerLine);
       WriteBuf(0, Size.Y-1, Size.X, 1, B);                  { Write the line }
     End;
END;

{--TMenuBox-----------------------------------------------------------------}
{  DrawBackGround -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB    }
{---------------------------------------------------------------------------}
PROCEDURE TMenuBox.DrawBackGround;
VAR X, Y, Y2: Integer; P : PMenuItem;
BEGIN
   Y2 := FontHeight DIV 2;                            { Intra offset }
   Y := FontHeight;                                   { Initial position }
   X := 3*FontWidth;                                  { 2 offset }
   Inherited DrawBackGround;                          { Call ancestor }
   If (Menu <> Nil) Then Begin                        { We have a menu }
     P := Menu^.Items;                                { Start on first }
     While (P <> Nil) Do Begin
       If (P^.Name = Nil) Then                        { Item has no string }
         BiColorRectangle(X, Y+Y2, RawSize.X-X,
           Y+Y2+1, White, DarkGray, True);            { Draw 3d line effect }
       Inc(Y, FontHeight);                            { Down one line }
       P := P^.Next;                                  { Next item now }
     End;
   End;
   BiColorRectangle(3, 3, RawSize.X-3, RawSize.Y-3,
     White, DarkGray, False);                         { Draw 3d effect }
   BiColorRectangle(5, 5, RawSize.X-5, RawSize.Y-5,
    White, DarkGray, True);                           { Draw 3d effect }
   BiColorRectangle(0, 0, RawSize.X, RawSize.Y,
     White, DarkGray, False);                         { Draw 3d effect }
END;

{--TMenuBox-----------------------------------------------------------------}
{  GetItemRectX -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB      }
{---------------------------------------------------------------------------}
PROCEDURE TMenuBox.GetItemRectX (Item: PMenuItem; Var R: TRect);
VAR X, Y: Integer; P: PMenuItem;
BEGIN
   Y := FontHeight;                                   { Initial y position }
   P := Menu^.Items;                                  { Initial item }
   While (P <> Item) Do Begin                         { Valid item }
     Inc(Y, FontHeight);                              { Inc position }
     P := P^.Next;                                    { Next item }
   End;
   X := 2 * FontWidth;                                { Left/Right margin }
   R.Assign(X, Y, RawSize.X - X, Y + FontHeight);     { Assign area }
END;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                        TMenuPopUp OBJECT METHODS                          }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{--TMenuPopUp---------------------------------------------------------------}
{  Init -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 15May98 LdB              }
{---------------------------------------------------------------------------}
CONSTRUCTOR TMenuPopup.Init (Var Bounds: TRect; AMenu: PMenu);
BEGIN
   Inherited Init(Bounds, AMenu, Nil);                { Call ancestor }
END;

{--TMenuPopUp---------------------------------------------------------------}
{  Done -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 15May98 LdB              }
{---------------------------------------------------------------------------}
DESTRUCTOR TMenuPopup.Done;
BEGIN
   If (Menu <> Nil) Then DisposeMenu(Menu);           { Dispose menu items }
   Inherited Done;                                    { Call ancestor }
END;

{--TMenuPopUp---------------------------------------------------------------}
{  HandleEvent -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 15May98 LdB       }
{---------------------------------------------------------------------------}
PROCEDURE TMenuPopup.HandleEvent (Var Event: TEvent);
VAR P: PMenuItem;
BEGIN
   Case Event.What Of
     evKeyDown: Begin
         P := FindItem(GetCtrlChar(Event.KeyCode));   { Find the item }
         If (P = Nil) Then P := HotKey(Event.KeyCode);{ Try hot key }
         If (P <> Nil) AND (CommandEnabled(P^.Command))
         Then Begin                                   { Command valid }
           Event.What := evCommand;                   { Command event }
           Event.Command := P^.Command;               { Set command value }
           Event.InfoPtr := Nil;                      { Clear info ptr }
           PutEvent(Event);                           { Put event on queue }
           ClearEvent(Event);                         { Clear the event }
         End Else If (GetAltChar(Event.KeyCode) <> #0)
           Then ClearEvent(Event);                    { Clear the event }
       End;
   End;
   Inherited HandleEvent(Event);                      { Call ancestor }
END;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                        TStatusLine OBJECT METHODS                         }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{--TStatusLine--------------------------------------------------------------}
{  Init -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB              }
{---------------------------------------------------------------------------}
CONSTRUCTOR TStatusLine.Init (Var Bounds: TRect; ADefs: PStatusDef);
BEGIN
   Inherited Init(Bounds);                            { Call ancestor }
   Options := Options OR ofPreProcess;                { Pre processing view }
   EventMask := EventMask OR evBroadcast;             { See broadcasts }
   GrowMode := gfGrowLoY + gfGrowHiX + gfGrowHiY;     { Set grow modes }
   Defs := ADefs;                                     { Set default items }
   FindItems;                                         { Find the items }
END;

{--TStatusLine--------------------------------------------------------------}
{  Load -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB              }
{---------------------------------------------------------------------------}
CONSTRUCTOR TStatusLine.Load (Var S: TStream);

   FUNCTION DoLoadStatusItems: PStatusItem;
   VAR Count: Integer; Cur, First: PStatusItem; Last: ^PStatusItem;
   BEGIN
     Cur := Nil;                                      { Preset nil }
     Last := @First;                                  { Start on first item }
     S.Read(Count, SizeOf(Count));                    { Read count }
     While (Count > 0) Do Begin
       New(Cur);                                      { New status item }
       Last^ := Cur;                                  { First chain part }
       If (Cur <> Nil) Then Begin                     { Check pointer valid }
         Last := @Cur^.Next;                          { Chain complete }
         Cur^.Text := S.ReadStr;                      { Read item text }
         S.Read(Cur^.KeyCode, SizeOf(Cur^.KeyCode));  { Keycode of item }
         S.Read(Cur^.Command, SizeOf(Cur^.Command));  { Command of item }
       End;
       Dec(Count);                                    { One item loaded }
     End;
     Last^ := Nil;                                    { Now chain end }
     DoLoadStatusItems := First;                      { Return the list }
   END;

   FUNCTION DoLoadStatusDefs: PStatusDef;
   VAR Count: Integer; Cur, First: PStatusDef; Last: ^PStatusDef;
   BEGIN
     Last := @First;                                  { Start on first }
     S.Read(Count, SizeOf(Count));                    { Read item count }
     While (Count > 0) Do Begin
       New(Cur);                                      { New status def }
       Last^ := Cur;                                  { First part of chain }
       If (Cur <> Nil) Then Begin                     { Check pointer valid }
         Last := @Cur^.Next;                          { Chain complete }
         S.Read(Cur^.Min, SizeOf(Cur^.Min));          { Read min data }
         S.Read(Cur^.Max, SizeOf(Cur^.Max));          { Read max data }
         Cur^.Items := DoLoadStatusItems;             { Set pointer }
       End;
       Dec(Count);                                    { One item loaded }
     End;
     Last^ := Nil;                                    { Now chain ends }
     DoLoadStatusDefs := First;                       { Return item list }
   END;

BEGIN
   Inherited Load(S);                                 { Call ancestor }
   Defs := DoLoadStatusDefs;                          { Retreive items }
   FindItems;                                         { Find the items }
END;

{--TStatusLine--------------------------------------------------------------}
{  Done -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB              }
{---------------------------------------------------------------------------}
DESTRUCTOR TStatusLine.Done;
VAR T: PStatusDef;

   PROCEDURE DisposeItems (Item: PStatusItem);
   VAR T: PStatusItem;
   BEGIN
     While (Item <> Nil) Do Begin                     { Item to dispose }
       T := Item;                                     { Hold pointer }
       Item := Item^.Next;                            { Move down chain }
       DisposeStr(T^.Text);                           { Dispose string }
       Dispose(T);                                    { Dispose item }
     End;
   END;

BEGIN
   While (Defs <> Nil) Do Begin
     T := Defs;                                       { Hold pointer }
     Defs := Defs^.Next;                              { Move down chain }
     DisposeItems(T^.Items);                          { Dispose the item }
     Dispose(T);                                      { Dispose status item }
   End;
   Inherited Done;                                    { Call ancestor }
END;


{--TStatusLine--------------------------------------------------------------}
{  GetPalette -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB        }
{---------------------------------------------------------------------------}
FUNCTION TStatusLine.GetPalette: PPalette;
{$IFDEF PPC_DELPHI3}                                  { DELPHI3+ COMPILER }
CONST P: String = CStatusLine;                        { Possible huge string }
{$ELSE}                                               { OTHER COMPILERS }
CONST P: String[Length(CStatusLine)] = CStatusLine;   { Always normal string }
{$ENDIF}
BEGIN
   GetPalette := @P;                                  { Return palette }
END;

{--TStatusLine--------------------------------------------------------------}
{  Hint -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB              }
{---------------------------------------------------------------------------}
FUNCTION TStatusLine.Hint (AHelpCtx: Word): String;
BEGIN
   Hint := '';                                        { Return nothing }
END;

{--TStatusLine--------------------------------------------------------------}
{  Draw -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB              }
{---------------------------------------------------------------------------}
PROCEDURE TStatusLine.Draw;
BEGIN
   DrawSelect(Nil);                                   { Call draw select }
END;

{--TStatusLine--------------------------------------------------------------}
{  Update -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 15May98 LdB            }
{---------------------------------------------------------------------------}
PROCEDURE TStatusLine.Update;
VAR H: Word; P: PView;
BEGIN
   P := TopView;                                      { Get topmost view }
   If (P <> Nil) Then H := P^.GetHelpCtx Else         { Top views context }
     H := hcNoContext;                                { No context }
   If (HelpCtx <> H) Then Begin                       { Differs from last }
     HelpCtx := H;                                    { Hold new context }
     FindItems;                                       { Find the item }
     DrawView;                                        { Redraw the view }
   End;
END;

{--TStatusLine--------------------------------------------------------------}
{  Store -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB             }
{---------------------------------------------------------------------------}
PROCEDURE TStatusLine.Store (Var S: TStream);

   PROCEDURE DoStoreStatusItems (Cur: PStatusItem);
   VAR Count: Integer; T: PStatusItem;
   BEGIN
     Count := 0;                                      { Clear count }
     T := Cur;                                        { Start on current }
     While (T <> Nil) Do Begin
       Inc(Count);                                    { Count items }
       T := T^.Next;                                  { Next item }
     End;
     S.Write(Count, SizeOf(Count));                   { Write item count }
     While (Cur <> Nil) Do Begin
       S.WriteStr(Cur^.Text);                         { Store item text }
       S.Write(Cur^.KeyCode, SizeOf(Cur^.KeyCode));   { Keycode of item }
       S.Write(Cur^.Command, SizeOf(Cur^.Command));   { Command of item }
       Cur := Cur^.Next;                              { Move to next item }
     End;
   END;

   PROCEDURE DoStoreStatusDefs (Cur: PStatusDef);
   VAR Count: Integer; T: PStatusDef;
   BEGIN
     Count := 0;                                      { Clear count }
     T := Cur;                                        { Current status item }
     While (T <> Nil) Do Begin
       Inc(Count);                                    { Count items }
       T := T^.Next                                   { Next item }
     End;
     S.Write(Count, 2);                               { Write item count }
     While (Cur <> Nil) Do Begin
       With Cur^ Do Begin
         S.Write(Cur^.Min, 2);                        { Write min data }
         S.Write(Cur^.Max, 2);                        { Write max data }
         DoStoreStatusItems(Items);                   { Store the items }
       End;
       Cur := Cur^.Next;                              { Next status item }
     End;
   END;

BEGIN
   TView.Store(S);                                    { TView.Store called }
   DoStoreStatusDefs(Defs);                           { Store status items }
END;

{--TStatusLine--------------------------------------------------------------}
{  HandleEvent -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB       }
{---------------------------------------------------------------------------}
PROCEDURE TStatusLine.HandleEvent (Var Event: TEvent);
VAR Mouse: TPoint; T, Tt: PStatusItem;

   FUNCTION ItemMouseIsIn: PStatusItem;
   VAR X, Xi: Word; T: PStatusItem;
   BEGIN
     ItemMouseIsIn := Nil;                            { Preset fail }
     If (Mouse.Y < 0) OR (Mouse.Y > FontHeight)       { Outside view height }
       Then Exit;                                     { Not in view exit }
     X := 0;                                          { Zero x position }
     T := Items;                                      { Start at first item }
     While (T <> Nil) Do Begin                        { While item valid }
       If (T^.Text <> Nil) Then Begin                 { Check valid text }
         Xi := X;                                     { Hold initial x value }
         X := Xi + TextWidth(' ' + T^.Text^ + ' ');   { Add text width }
         If (Mouse.X >= Xi) AND (Mouse.X < X)
         Then Begin
           ItemMouseIsIn := T;                        { Selected item }
           Exit;                                      { Now exit }
         End;
       End;
       T := T^.Next;                                  { Next item }
     End;
   END;

BEGIN
   Inherited HandleEvent(Event);                      { Call ancestor }
   Case Event.What Of
     evMouseDown: Begin
         T := Nil;                                    { Preset ptr to nil }
         Repeat
           Mouse.X := Event.Where.X - RawOrigin.X;    { Local x position }
           Mouse.Y := Event.Where.Y - RawOrigin.Y;    { Local y position }
           Tt := ItemMouseIsIn;                       { Find selected item }
           If (T <> Tt) Then                          { Item has changed }
             DrawSelect(Tt);                          { Draw new item }
           T := Tt                                    { Transfer item }
         Until NOT MouseEvent(Event, evMouseMove);    { Mouse stopped moving }
         If (T <> Nil) AND CommandEnabled(T^.Command) { Check cmd enabled }
         Then Begin
           Event.What := evCommand;                   { Command event }
           Event.Command := T^.Command;               { Set command value }
           Event.InfoPtr := Nil;                      { No info ptr }
           PutEvent(Event);                           { Put event on queue }
         End;
         ClearEvent(Event);                           { Clear the event }
         DrawSelect(Nil);                             { Clear the highlight }
       End;
     evKeyDown: Begin                                 { Key down event }
         T := Items;                                  { Start on first item }
         While (T <> Nil) Do Begin                    { For each valid item }
           If (Event.KeyCode = T^.KeyCode) AND        { Check for hot key }
           CommandEnabled(T^.Command) Then Begin      { Check cmd enabled }
             Event.What := evCommand;                 { Change to command }
             Event.Command := T^.Command;             { Set command value }
             Event.InfoPtr := Nil;                    { Clear info ptr }
             Exit;                                    { Now exit }
           End;
           T := T^.Next;                              { Next item }
         End;
       End;
     evBroadcast:
       If (Event.Command = cmCommandSetChanged) Then  { Command set change }
         DrawView;                                    { Redraw view }
   End;
END;

{***************************************************************************}
{                    TStatusLine OBJECT PRIVATE METHODS                     }
{***************************************************************************}

{--TStatusLine--------------------------------------------------------------}
{  FindItems -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB         }
{---------------------------------------------------------------------------}
PROCEDURE TStatusLine.FindItems;
VAR P: PStatusDef;
BEGIN
   P := Defs;                                         { First status item }
   While (P <> Nil) AND ((HelpCtx < P^.Min) OR
   (HelpCtx > P^.Max)) Do P := P^.Next;               { Find status item }
   If (P = Nil) Then Items := Nil Else
     Items := P^.Items;                               { Return found item }
END;

{--TStatusLine--------------------------------------------------------------}
{  DrawSelect -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 11May98 LdB        }
{---------------------------------------------------------------------------}
PROCEDURE TStatusLine.DrawSelect (Selected: PStatusItem);
VAR I, L: Integer; Color, CSelect, CNormal, CSelDisabled, CNormDisabled: Word;
    HintBuf: String; B: TDrawBuffer; T: PStatusItem;
BEGIN
   CNormal := GetColor($0301);                        { Normal colour }
   CSelect := GetColor($0604);                        { Select colour }
   CNormDisabled := GetColor($0202);                  { Disabled colour }
   CSelDisabled := GetColor($0505);                   { Select disabled }
   MoveChar(B, ' ', Byte(CNormal), 80);               { Clear the buffer }
   T := Items;                                        { First item }
   I := 0;                                            { Clear the count }
   L := 0;
   While (T <> Nil) Do Begin                          { While valid item }
     If (T^.Text <> Nil) Then Begin                   { While valid text }
       L := CStrLen(' '+T^.Text^+' ');                { Text length }
       If CommandEnabled(T^.Command) Then Begin       { Command enabled }
         If T = Selected Then Color := CSelect        { Selected colour }
           Else Color := CNormal                      { Normal colour }
       End Else
         If T = Selected Then Color := CSelDisabled   { Selected disabled }
           Else Color := CNormDisabled;               { Disabled colour }
       MoveCStr(B[I], ' '+T^.Text^+' ', Color);       { Move text to buf }
       Inc(I, L);                                     { Advance position }
     End;
     T := T^.Next;                                    { Next item }
   End;
   HintBuf := Hint(HelpCtx);                          { Get hint string }
   If (HintBuf <> '') Then Begin                      { Hint present }
     {$IFNDEF OS_WINDOWS}
     MoveChar(B[I], #179, Byte(CNormal), 1);          { '|' char to buffer }
     {$ELSE}
     MoveChar(B[I], #166, Byte(CNormal), 1);          { '|' char to buffer }
     {$ENDIF}
     Inc(I, 2);                                       { Move along }
     MoveStr(B[I], HintBuf, Byte(CNormal));           { Move hint to buffer }
     I := I + Length(HintBuf);                        { Hint length }
   End;
   WriteLine(0, 0, I, 1, B);                          { Write the buffer }
   If AdvancedMenus Then BicolorRectangle(0, 0,
     RawSize.X, RawSize.Y, White, DarkGray, False);   { Add 3d effect }
END;

{***************************************************************************}
{                            INTERFACE ROUTINES                             }
{***************************************************************************}

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                           MENU INTERFACE ROUTINES                         }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{---------------------------------------------------------------------------}
{  NewMenu -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 14May98 LdB           }
{---------------------------------------------------------------------------}
FUNCTION NewMenu (Items: PMenuItem): PMenu;
VAR P: PMenu;
BEGIN
   New(P);                                            { Create new menu }
   If (P <> Nil) Then Begin                           { Check valid pointer }
     P^.Items := Items;                               { Hold item list }
     P^.Default := Items;                             { Set default item }
   End;
   NewMenu := P;                                      { Return menu }
END;

{---------------------------------------------------------------------------}
{  DisposeMenu -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 14May98 LdB       }
{---------------------------------------------------------------------------}
PROCEDURE DisposeMenu (Menu: PMenu);
VAR P, Q: PMenuItem;
BEGIN
   If (Menu <> Nil) Then Begin                        { Valid menu item }
     P := Menu^.Items;                                { First item in list }
     While (P <> Nil) Do Begin                        { Item is valid }
       If (P^.Name <> Nil) Then Begin                 { Valid name pointer }
         DisposeStr(P^.Name);                         { Dispose of name }
         If (P^.Command <> 0) Then
           DisposeStr(P^.Param) Else                  { Dispose parameter }
           DisposeMenu(P^.SubMenu);                   { Dispose submenu }
       End;
       Q := P;                                        { Hold pointer }
       P := P^.Next;                                  { Move to next item }
       Dispose(Q);                                    { Dispose of item }
     End;
     Dispose(Menu);                                   { Dispose of menu }
   End;
END;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                             MENU ITEM ROUTINES                            }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{---------------------------------------------------------------------------}
{  NewLine -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 14May98 LdB           }
{---------------------------------------------------------------------------}
FUNCTION NewLine (Next: PMenuItem): PMenuItem;
VAR P: PMenuItem;
BEGIN
   New(P);                                            { Allocate memory }
   If (P <> Nil) Then Begin                           { Check valid pointer }
     P^.Next := Next;                                 { Hold next menu item }
     P^.Name := Nil;                                  { Clear name ptr }
     P^.HelpCtx := hcNoContext;                       { Clear help context }
   End;
   NewLine := P;                                      { Return new line }
END;

{---------------------------------------------------------------------------}
{  NewItem -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 14May98 LdB           }
{---------------------------------------------------------------------------}
FUNCTION NewItem (Name, Param: TMenuStr; KeyCode: Word; Command: Word;
  AHelpCtx: Word; Next: PMenuItem): PMenuItem;
VAR P: PMenuItem; R: TRect; T: PView;
BEGIN
   If (Name <> '') AND (Command <> 0) Then Begin
     New(P);                                          { Allocate memory }
     If (P <> Nil) Then Begin                         { Check valid pointer }
       P^.Next := Next;                               { Hold next item }
       P^.Name := NewStr(Name);                       { Hold item name }
       P^.Command := Command;                         { Hold item command }
       R.Assign(1, 1, 10, 10);                        { Random assignment }
       T := New(PView, Init(R));                      { Create a view }
       If (T <> Nil) Then Begin
         P^.Disabled := NOT T^.CommandEnabled(Command);
         Dispose(T, Done);                            { Dispose of view }
       End Else P^.Disabled := True;
       P^.KeyCode := KeyCode;                         { Hold item keycode }
       P^.HelpCtx := AHelpCtx;                        { Hold help context }
       P^.Param := NewStr(Param);                     { Hold parameter }
     End;
     NewItem := P;                                    { Return item }
   End Else NewItem := Next;                          { Move forward }
END;

{---------------------------------------------------------------------------}
{  NewSubMenu -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 14May98 LdB        }
{---------------------------------------------------------------------------}
FUNCTION NewSubMenu (Name: TMenuStr; AHelpCtx: Word; SubMenu: PMenu;
  Next: PMenuItem): PMenuItem;
VAR P: PMenuItem;
BEGIN
   If (Name <> '') AND (SubMenu <> Nil) Then Begin
     New(P);                                          { Allocate memory }
     If (P <> Nil) Then Begin                         { Check valid pointer }
       P^.Next := Next;                               { Hold next item }
       P^.Name := NewStr(Name);                       { Hold submenu name }
       P^.Command := 0;                               { Clear item command }
       P^.Disabled := False;                          { Item not disabled }
       P^.HelpCtx := AHelpCtx;                        { Set help context }
       P^.SubMenu := SubMenu;                         { Hold next submenu }
     End;
     NewSubMenu := P;                                 { Return submenu }
   End Else NewSubMenu := Next;                       { Return next item }
END;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                          STATUS INTERFACE ROUTINES                        }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{---------------------------------------------------------------------------}
{  NewStatusDef -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 15May98 LdB      }
{---------------------------------------------------------------------------}
FUNCTION NewStatusDef (AMin, AMax: Word; AItems: PStatusItem;
ANext:PStatusDef): PStatusDef;
VAR T: PStatusDef;
BEGIN
   New(T);                                            { Allocate memory }
   If (T <> Nil) Then Begin                           { Check valid pointer }
     T^.Next := ANext;                                { Set next item }
     T^.Min := AMin;                                  { Hold min value }
     T^.Max := AMax;                                  { Hold max value }
     T^.Items := AItems;                              { Hold item list }
   End;
   NewStatusDef := T;                                 { Return status }
END;

{---------------------------------------------------------------------------}
{  NewStatusKey -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 15May98 LdB      }
{---------------------------------------------------------------------------}
FUNCTION NewStatusKey (AText: String; AKeyCode: Word; ACommand: Word;
  ANext: PStatusItem): PStatusItem;
VAR T: PStatusItem;
BEGIN
   New(T);                                            { Allocate memory }
   If (T <> Nil) Then Begin                           { Check valid pointer }
     T^.Text := NewStr(AText);                        { Hold text string }
     T^.KeyCode := AKeyCode;                          { Hold keycode }
     T^.Command := ACommand;                          { Hold command }
     T^.Next := ANext;                                { Pointer to next }
   End;
   NewStatusKey := T;                                 { Return status item }
END;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                           OBJECT REGISTER ROUTINES                        }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{---------------------------------------------------------------------------}
{  RegisterMenus -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 15May98 LdB     }
{---------------------------------------------------------------------------}
PROCEDURE RegisterMenus;
BEGIN
   RegisterType(RMenuBar);                            { Register bar menu }
   RegisterType(RMenuBox);                            { Register menu box }
   RegisterType(RStatusLine);                         { Register status line }
   RegisterType(RMenuPopup);                          { Register popup menu }
END;

END.
{
 $Log: menus.pas,v $
 Revision 1.16  2002/10/17 11:24:17  pierre
  * Clean up the Load/Store routines so they are endian independent

 Revision 1.15  2002/09/07 15:06:37  peter
   * old logs removed and tabs fixed

 Revision 1.14  2002/06/10 18:41:26  pierre
  + add Submenu recognition sign

 Revision 1.13  2002/05/30 06:58:28  pierre
  * fix grpah related menubar draw issues

 Revision 1.12  2002/05/29 19:36:52  pierre
  * fix UseFixedFont related code

 Revision 1.11  2002/05/21 10:53:25  pierre
  * fix graphical separation lines

}


syntax highlighted by Code2HTML, v. 0.9.1