                               Pull-Down Menus

                                  "Bell, book, and  candle shall not
                                   drive me back, when gold and silver
                                   becks me to come on."
                                               King John, Shakespeare

Introduction

          We are excited. The pull-down menu features offered in Gold
     are the most flexible and easy to use in the market place. In this
     chapter you will learn about the following features of Gold's
     pull-down menus and status bars:

          Multiple menu styles are supported, including traditional,
          chiseled, and a Microsoft Windows look-alike.

          Pull-down menus support keyboard, mouse and hotkey input.

          Menus can be nested many levels deep.

          Separators and grayed items can be easily incorporated.


     Figure 11.1
     A Pull-Down Menu
     with a
     Status Bar

The Architecture of a Pull-Down Menu

          A pull-down menu is actually a collection of separate menus
     bound together. There is always one menu bar in a pull-down menu,
     even if there is only one item on the bar. The menu bar defines the
     menu items which appear across the top of the pull-down menu, i.e.
     the items which are visible even when the menu is inactive.

          Each item on the menu bar usually has a pop-up menu item
     defined. Any item in a pop-up menu can point to another pop-up menu
     thereby providing multiple levels of sub-menus.

          This scheme of combining a menu bar with several pop-up menus
     to create a pull-down menu is illustrated in Figure 11.2.


Building a Pull-Down menu

          Enough theory; let's create a menu. Creating a menu is like
     baking a cake, all you need to do is follow the recipe:

     	Declare the menu variables
     	Construct a menu bar with at least one item.
     	Construct as many pop-up menus as you need.
     	Activate the menu when a menu key is pressed in the application.
     	Call a sub-routine based on the user's menu selection.

Creating Menu Variables

          Every pull-down menu has one menu bar. The menu bar is defined
     in a variable of type Bar. Additionally, there is one pop-up menu
     for each menu bar which has related options. Each menu pop-up is
     define in a variable of type PopUp.

          The following declaration is extracted from DEMPLL2.PAS which
     defines a pull-down menu with two items on the menu bar, and two
     pop-up menus.

          var
             MainMenu: Bar;
             FileMenu: PopUp;
             OptionsMenu: PopUp;

Constructing the Menu Bar

          A variable of type Bar defines the content of the menu bar. To
     construct a menu bar, the menu bar must first be initialized with
     the InitBar procedure, and then items are added to the menu bar
     with the BarAddItem procedure. These two procedures are defined as
     follows:

     InitBar(var P: Bar);

          Sets a menu bar to the default values. This procedure must be
     called prior to calling BarAddItem. The procedure is passed the
     menu bar variable that will be initialized.

     BarAddItem(var M: Bar; Item:string; ID:integer; HK,AltHK:word;
                                           Desc:string; PopUp:pointer);

          Adds an item to the menu bar. The procedure is passed the menu
     bar variable, the text of the menu bar entry, the ID that will be
     returned when the user presses help or selects the menu item, two
     hotkeys (discussed below), the long description, and finally a
     pointer to a pop-menu variable (if a pop-up is to be displayed when
     the menu bar item is selected).

Managing Hotkeys

          There are many different ways that a user can make a choice
     from a pull-down menu: using the cursor keys to select an item and
     pressing Enter, using the mouse and double-clicking on the menu
     item, or pressing a hotkey. Gold supports three different levels of
     hotkey.

          As defined above, BarAddItem accepts two hotkey arguments. The
     first hotkey is the upper-case letter that can be pressed when the
     menu bar is active and the pop-up menus are not displayed. (This
     state occurs when you press F10 in the BP7 environment.) If you
     press the appropriate letter, Gold will jump to the menu item which
     was defined with that hotkey.

          The second hotkey passed to BarAddItem defines an alternate
     key which can be pressed whenever a pop-up is displayed (no matter
     how nested). The de facto standard is to use an Alt-letter key
     sequence for the second hotkey.

          For example, the a menu item define as "File" might have the
     letter F as the first hot key (70) and Alt-F as the second hotkey
     (289).

          To visibly indicate to the user which letter is the hotkey
     letter, use the "~" character to instruct gold to highlight the
     letter, e.g. '~F~ile'. This technique works for the menu bar items
     as well as pop-up items.

          The third type of hotkey is defined globally and is not
     directly associated with a specific menu item. For example, in BP7,
     the File Open menu item is the hotkey F2.  To define these
     additional hotkeys, use the procedure BarAddHK which is defined as
     follows:

     BarAddHK(var P: Bar; K:word; HotID:integer);

          Defines a general hotkey. The procedure is passed the menu bar
     variable, the hotkey, and the ID which will be returned when the
     hotkey is pressed.

Example

          Listed below is an extract from DEMPLL2.PAS which initializes
     a two item menu bar and defines some extra hotkeys:

          procedure DefineMainMenu;
          {}
          begin
             InitBar(MainMenu);
             BarAddItem(MainMenu,'~F~ile',100,70,289,
                        'File management commands (open, new,
                        etc).',@FileMenu);
             BarAddItem(MainMenu,'~O~ptions',800,79,280,
                        'Set defaults for compiler, editor,
                        mouse, debugger, etc.',@OptionsMenu);
             BarAddHK(MainMenu,317,102); {F3 - File Open}
             BarAddHK(MainMenu,316,103); {F2 - File Save}
             BarAddHK(MainMenu,301,110); {Alt-X - quit}
          end; { DefineMainMenu }

          Notice that the last argument on the File menu constructor is
     @FileMenu. This informs Gold that the pop-up menu FileMenu should
     be displayed when the user selects File from the main menu bar.
     Similarly, the Options menu bar item will call the pop-up
     OptionsMenu.

Constructing the Menu Pop-Ups

          The process of constructing a pop-up menu is very similar to
     the process just described for menu bars. A variable of type PopUp
     defines the content of the pop-up menu. To construct a pop-up menu,
     the menu must first be initialized with the InitPopUp procedure,
     and then items are added to the menu with the PopUpAddItem
     procedure. These two procedure are defined as follows:

     InitPopup(var M:PopUp);

          Sets a pop-up menu to the default values. This procedure must
     be called prior to calling PopUpAddItem. The procedure is passed
     the popup menu variable that is to be initialized.

     PopupAddItem(var P:PopUp;Item:string; ID:integer; HK:word;
                                       Desc:string; ChildMenu:pointer);

          Adds an item to the menu. The procedure is passed the pop-up
     menu variable, the text of the menu item, the ID that will be
     returned when the user presses help or selects the menu item, a
     hotkey which can be used to select the item when the pop-up has
     focus, the long description, and finally a pointer to another popup
     variable if a pop-up is to be displayed when the menu  item is
     selected -- pass nil if there is no submenu.


          Listed below is a PopupAddItem statement which defines the
     "New " item on a file menu:

     PopUpAddItem(FileMenu,'~N~ew',101,78,'Create a new file in a new
                                                     Edit window',nil);

Defining Separators, Grayed Items and Right Justified Text

          By adding some reserved characters to the item text, you can
     exert additional control over the item format.

          Adding a Separator -- if the menu text is defined as a single
     minus character "-", Gold will insert a menu separator instead of a
     normal item. The other parameters are ignored, and can be set to
     nulls. For example:

     PopUpAddItem(FileMenu,'-',0,0,'',nil);

          Graying an Item -- later you will read about the SetActive
     procedures for making items grayed or selectable. An easy way to
     make a menu item non-selectable is to insert the exclamation
     character "!" in the first character of the item text. For example:

     PopUpAddItem(EditMenu,'!~R~edo',202,82,'Redo the previously undone
                                                 editor operation',nil);

          Right Justifying Text -- Traditionally, global hotkeys are
     displayed right  justified in the text of the menu item. To save
     you the chore of entering the correct number of spaces to right
     justify the hot key description, simply insert a colon ":" in the
     item text. All characters to the right of the colon will be right
     justified.

     PopUpAddItem(FileMenu,'E~x~it:Alt-X',110,88,'~Exit~ Turbo
                                                         Pascal',nil);

Example

          The following extract from DEMPLL2.PAS illustrates all the
     above techniques, and shows how a file menu might be defined:

          procedure DefineFilePopUp;
          {}
          begin
             InitPopUp(FileMenu);
             PopUpAddItem(FileMenu,'~N~ew',101,78,'Create a new
                          file in a new Edit window',nil);
             PopUpAddItem(FileMenu,'~O~pen...:F3',102,79,'Locate
                          and open in an Edit window',nil);
             PopUpAddItem(FileMenu,'~S~ave:F2',103,83,'Save the
                          file in the active Edit window',nil);
             PopUpAddItem(FileMenu,'Save ~a~s...',104,65,'Save
                          the current file under a different name
                          directory or drive',nil);
             PopUpAddItem(FileMenu,'Save a~l~l',105,76,'Save all
                          modified files',nil);
             PopUpAddItem(FileMenu,'-',0,0,'',nil); {separator}
             PopUpAddItem(FileMenu,'~C~hange dir...',106,67,
                          'Choose a new default directory',nil);
             PopUpAddItem(FileMenu,'~P~rint',107,80,'Print the co
                          ntents of the active Edit window',nil);
             PopUpAddItem(FileMenu,'P~r~inter setup',108,82,                     'Choose printer filter to
                          use for printing',nil);
             PopUpAddItem(FileMenu,'~D~os shell',109,68,
                          'Temporily exit to DOS',nil);
             PopUpAddItem(FileMenu,'E~x~it:Alt-X',110,88,'~Exit~
                          Turbo Pascal',nil);
          end; { DefineFilePopUp }


          Look at the demo files DEMPLL1.PAS through DEMPLL4.PAS to see
     more examples of menu constructions.

Activating a Pull-Down Menu

          The GOLDDESK unit provides a very powerful and flexible way of
     managing input to an application using a "desktop" just like the
     desktop used in BP7. If you have not used a desktop, consider
     reading the next chapter before deciding to roll-your-own desktop
     using the primary menu input functions. You will probably find that
     the desktop meets all of your needs (and then some).

          Gold, however, does not force you to use the desktop. You can
     always manage input to the application yourself and pass
     menu-related keystrokes to the menu for processing. This section
     describes how.

Invoking the Menu

          The simplest way to pass control to the menu is to call the
     function ActivatePullmenu. The function is passed the menu bar
     variable and returns an integer indicating the users selection. For
     example:

     Choice := ActivatePullmenu(MainMenu);

          If the user escaped from the menu without making a selection,
     a zero is returned.

Passing keystrokes to the Menu

          If a pull-down menu is to form the main task manager for an
     application, the menu display functions should be placed inside a
     loop that prompts the user to make a menu selection and then
     responds to the selection (usually in the form of a case
     statement).

          The GOLDMENU unit includes the function IsPullkey which is
     passed the menu variable along with Gold's standard three entries
     which define a keystroke, i.e. the key, and the mouse X,Y
     coordinates. The function returns true if the input should be
     handled by the pull-down menu. When a true is returned, the
     application should pass the keystroke to the menu using the
     PullPushKey function -- this function simply instructs Gold to
     process the passed keystroke rather than wait for new user input.

     The following code fragment illustrates this basic technique:

          with KeyVars do
             repeat
                GetInput;
                if IsPullKey(MainMenu,LastKey,LastX,LastY) then
                begin
                   Choice := PullPushKey(MainMenu,LastKey,LastX,
                                          LastY);
                   case Choice of
                      101:;
                      102:;
                      103:;
                      ........
                    end; {case}

                end;
             until Choice = 999;

          The following example is an extract of DEMPLL3.PAS which uses
     the plain ActivateMenu function if the user presses the slash key
     or F10, and uses PullPushkey if the input is menu related.

          DefineMenus;
          DrawBar(MainMenu);
          MouseShow(true);
          CursorOff;
          repeat
             Choice := 0;
             GetInput;
             with KeyVars do
             begin
               if (LastKey = 47) {/}
               or (LastKey = 324) {F10} then
                  Choice := ActivatePullMenu(mainmenu)
               else if IsPullKey(MainMenu, LastKey, LastX, LastY) then
                  Choice := PullPushKey(MainMenu, LastKey, LastX,LastY);
               case choice of
                  101:;  {call the appropriate functions}
                  102:;
               end;
               if (Choice <> 0) and (Choice <> 110) then
                  PromptOK(' Gold ','You chose menu ID '+
                                                  IntToStr(Choice));
             end;
          until Choice = 110; {the exit choice}
          MouseShow(false);
          DisposeMenus;

          Don't forget that the desktop can take care of all of this
     input code for you. Further details are provided in the next
     chapter.

Disposing of Menu Memory

          The menu bar and the menu pop-ups use memory on the heap to
     store the menu details. When a menu is no longer required, the menu
     structures must be disposed of by calling DestroyBar and
     DestroyPopup. The following code is taken from DEMPLL3.PAS:

          procedure DisposeMenus;
          {}
          begin
             DestroyBar(MainMenu);
             DestroyPopUp(FileMenu);
             DestroyPopUp(OptionsMenu);
             DestroyPopUp(EnvironmentMenu);
          end; { DisposeMenus }

Customizing the Menu's Appearance

          Like many other Gold offerings, menus use industry standard
     defaults to make it easy to create a professional and contemporary
     application. However, various aspects of the menus can be
     customized to meet individual tastes.

Customizing the Menu Style

          The most dramatic way to change the overall menu appearance is
     to change the menu style. The menu bar variable is actually a
     record which contains a field named Style. The Style variable can
     have any value in the range 1 to 4 as follows:

          Style  Description

          1      A BP7-style menu with a single line border.
          2      Similar to style 1, but the pop-up menu borders have a
                 chiseled affect. This is the default style.
          3      This style takes advantage of Gold's custom characters,
                 and draws window borders along the outermost edge of
                 the pop-ups.
          4      Draws a Microsoft Windows look-alike. This style can
                 take advantage of Gold's custom characters.

          The default menu style is defined by the variable
     MenuVars.PullStyle. -- its value is assigned to a menu bar when it
     is initialized.

          You can experiment with the menu styles by running DEMPLL4.PAS
     and modifying the Mainmenu.Style value.

Customizing Menu Colors

          You guessed it, you can customize any of the menu colors by
     calling GoldSetColor. The following elements of TINT affect the
     menu colors:

          PullHiHot
          PullHi
          PullNormHot
          PullNorm
          PullOff
          PullMsgHot
          PullMsg
          PullBorder1
          PullBorder2

          The following code is an extract from DEMCAL5.PAS which
     customizes the menu colors:

          procedure CustomizeColors;
          {}
          begin
             GoldSetColor(PullHiHot,YellowOnBlue);
             GoldSetColor(PullHi,WhiteOnBlue);
             GoldSetColor(PullNormHot,YellowOnMagenta);
             GoldSetColor(PullNorm,WhiteOnMagenta);
             GoldSetColor(PullOff,LightgrayOnMagenta);
             GoldSetColor(PullMsgHot,YellowonMagenta);
             GoldSetColor(PullMsg,WhiteonMagenta);
             GoldSetColor(PullBorder1,BlackOnmagenta);
             GoldSetColor(PullBorder2,CyanOnMagenta);
          end; { CustomizeColors }

Customizing Other Menu Components

          A menu bar (record) variable contains additional fields
     allowing you to customize the menu bar location, the long prompt
     location, as well as optional characters used to designate special
     items and highlight the active item.

Controlling the Menu Location

          It is normal for the menu to occupy the top row of the screen
     (or the top two rows for the Windows-style menu). However you can
     position the menu where you want. The menu bar variables TopX and
     TopY identify the location of the top left of the main menu bar.
     When a menu bar is initialized, with InitBar, the values of TopX
     and TopY are set to 1.

Controlling the Message Location

          By default, the long description of the highlighted topic is
     displayed at the bottom of the display. The message location can be
     controlled by modifying the following fields of the menu bar
     record: DescX1, DescX2 and DescY. These control the starting column
     of the long description, the ending column of the long description,
     and the row on which the description will be displayed.

          If you are using a menu style of 4, the menu description
     should be displayed on the top of the screen. The following extract
     from DEMPLL4.PAS shows how to adjust the message location for
     various styles:

        if MainMenu.Style = 4 then
        begin
           ClearLine(1,WhiteOnBlue);
           ClearLine(2,BlackOnLightgray);
           MainMenu.DescX1 := 4;
           MainMenu.DescX2 := 80;
           MainMenu.DescY := 1;
           GoldSetColor(PullMsgHot, whiteonblue);
           GoldSetColor(PullMsg, lightgrayonblue);
        end

Redefining Menu Display Characters

          Earlier you learned that if the item text was a minus sign a
     separator would be inserted in the pop-up. You also learned that an
     item starting with an exclamation would be initially
     non-selectable, and any text to the right of a colon would be right
     justified.

          These special characters are not hard coded, and can be
     configured to use other characters. To change the characters,
     simply assign new values to the following three variables:
     MenuVars.Separator, MenuVars.InActiveChar and MenuVars.TabChar.

          By assigning different characters to the variables
     MenuVars.PullLeft and MenuVars.PullRight you can make the active
     (or highlighted) item standout more clearly.

          Finally, the character used to indicate that an item has a
     sub-menu defaults to a right pointing chevron. You can use a
     different character by assigning a new character value to the
     MenuVars.PullSubIndicator variable.

Adding Menu Help

          You can instruct Gold to call a custom help procedure to
     provide menu context sensitive help. All you have to do is create a
     procedure following some specific rules and call the procedure
     AssignMenuHelpHook to instruct Gold to call your procedure every
     time the user requests help while the menu has focus.

          For a procedure to be eligible as a menu help hook it must
     adhere to the following rules:

          The procedure must be declared as a far procedure at the root
          level. Refer to the section Understanding Hooks in chapter 3
          for further information.

          The procedure must be declared with one parameter of type
          integer. This variable identifies the ID of the active item at
          the time the user requested help.

     The following procedure declaration follows these rules:

          {$F+}
          procedure MenuHelp(ID:integer);
          {}
          begin
             PromptOK(' Help! ','You asked for help on item ID: '
                       +IntToStr(ID));
          end; { MenuHelp }
          {$F-}

          The following procedure is then called to instruct Gold to
     call your procedure after each input:

     AssignMenuHelpHook(var M: Bar; Proc:PullHook);;

          Instructs Gold to call the specified procedure when the user
     requests help during menu selection.

          When a help hook is implemented, Gold automatically adds a
     help string to the long description line, i.e. at coordinates
     (MenuVars.DescX1, MenuVars.MsgY). The user can click on this area
     with the mouse, to access the help.

          International users note that the actual text of the help
     string is defined in the variable MenuVars.HelpStr and can be
     modified as necessary. By default the help key is F1; this too can
     be customized -- just assign an new keystroke value to the variable
     MenuVars.HelpKey.

          If, subsequently, you want to remove the help hook, call the
     procedure RemoveMenuHelpHook.

          The demo file DEMPLL4.PAS shows how a help hook can be added
     to a pull-down menu.

Utilizing Menu Hind Hooks

          If you want to write some additional information to the screen
     based on the active menu item, you should take advantage of the
     pull-down menu's hind hook. A hind hook is called once when the
     menu is first displayed, and every time a keystroke or mouse click
     is processed by the menu.

          For a procedure to be eligible as a pull-down menu hind hook
     it must adhere to the following rules:

          The procedure must be declared as a far procedure at the root
          level. Refer to the section Understanding Hooks in chapter 3
          for further information.

          The procedure must be declared with one parameter of type
          integer. This variable identifies the ID of the active item.

          Just like the help hook described above, the hind hook can be
     assigned and removed using AssignMenuHindHook and
     RemoveMenuHindHook, respectively.

Changing Menu Settings On-The-Fly

          Typically, you will create a pull-down menu when the
     application is started, and destroy the menu when the application
     is closed. During the session, you can change the look and feel of
     the menu (without having to destroy and rebuild the menu) by using
     the following procedures:

     BarSetActive(var M: Bar; ID:integer; On: boolean);

          Makes a menu bar item active or inactive.

     BarChangeItem(var M: Bar; ID:integer; Item,Desc:string;
                       HK,AltHK:word; NewID:integer; PopUp:pointer);

          Redefines all the components of a menu bar item.

     BarChangeText(var M: Bar; ID:integer; Item,Desc:string);

          Redefines the short and long descriptions of a menu bar item.

     BarDelItem(var M: Bar; ID:integer);

          Removes an item from a menu bar.

     BarDelHK(var P: Bar; K:word);

          Removes a global hot key.

     PopUpSetActive(var P:PopUp; ID:integer; On: boolean);

     Makes a pop-up menu item active or inactive.

     PopUpChangeItem(var P:PopUp; ID:integer; Item,Desc:string;HK:word;
                                     NewID:integer; ChildMenu:pointer);

          Redefines all the components of a pop-up menu item.

     PopUpChangeText(var P:PopUp; ID:integer; Item,Desc:string);

          Redefines the short and long descriptions of a pop-up menu
     item.

     PopUpDelItem(var P:PopUp; ID:integer);

          Removes an item from a pop-up menu.

Managing Status Bars

          The good news is that you already know how to create a status
     bar. A Status bar is actually a menu bar positioned at the bottom
     of the screen. You'll learn more about status bars in the next
     chapter.

     Error Management

          The pull down menu structures allocate memory on the heap and
     so have the potential to fail. For example, there may be
     insufficient free memory to add a menu bar or pop-up item. After
     calling the BarAddItem and PopupAddItem procedures be sure to call
     the function LastMenuError to see if the item addition was
     successful.
