Release 10.1A: OpenEdge Development:
Progress 4GL Handbook

> ## Defining a menu bar for the sample window

> To see how menus work, you can add one to the `h-CustOrderWin3.w` window. As it does with other objects, the AppBuilder can define most of the statements you need for your menu, using a special menu builder tool. Once you’ve completed the menu, you can examine the syntax to confirm the exact 4GL statements needed to do the job.

> | To define a menu bar for the sample window:

> 1. Open `h-CustOrderWin3.w` and save a new version as `h-CustOrderMenu.w`.

> 2. Click the Object Properties button for the window.

> Remember that if the window’s frame or some other object is selected, you can use the key CTRL+Click sequence to make the window the currently selected object in the AppBuilder.

> 3. In the Object Properties dialog box, click the Menu Bar button .

> 4. In the menu bar’s property sheet, call the Menu m_Custwin.

> You’ll add the following submenus to the menu:

> - One named Buttons that duplicates what the radio set and the First, Next, Prev, and Last buttons do.
> - One named Show that duplicates the Show Warehouse Info toggle box.
> - One named Help that you can use to display information about the window.
> 5. Define the first of these submenus by entering a Label of Buttons. When you tab out of that field, a default Object name of m_Buttons is provided for you:

> You can define all the levels of submenu and menu items together in this property sheet. To indicate the hierarchy, use the << and >> buttons to indent one item relative to the one above it.

> 6. Click the Insert button to add three new menu items in the Buttons submenu.

> 7. To indicate that they are menu items of the Buttons submenu, click the >> button to indent the first of the items. The others indent automatically.

> 8. Give the three items labels of No &First, Last, No &Prev, and Allow &All, respectively. Accept the default object names provided for you.

> The ampersand in each label represents a menu mnemonic. The mnemonics let the user press a sequence of keys, each beginning with the ALT key, to select any menu item from the hierarchy. The ampersand precedes the mnemonic for each of these menu items. By default the top-level submenus are given mnemonics of their first letters, so when you’re done the key sequence ALT+B, ALT+F selects the No First/Last menu item. When you press the ALT key the mnemonics are underlined to let you see what key sequences are available to you. (Showing the mnemonic underscores after the user presses ALT is now the Microsoft standard and OpenEdge follows it.)

> If you define a menu mnemonic key combination that duplicates an accelerator, the accelerator takes precedence.

> 9. Click the Rule button to insert a rule following these three items.

> 10. Click the Insert button and define an item with a label of Navigation:

> This new item is a submenu. The menu bar property sheet is flexible enough to allow you to define the entire hierarchy from the top down. It determines that this is a submenu when you add further nested items to it. If you need to, you can click the Up and Down buttons to rearrange the order of items in the menu.

> 11. Click Insert followed by the >> button to add an item nested under Navigation, with a label of First.

> 12. Click the Key button to enter the dialog box that lets you define an accelerator key sequence for the menu item.

> 13. Press the up arrow on the keyboard to register that as the accelerator key for the First button. Its keyboard name is displayed as `CURSOR-UP`:

> 14. Click OK to accept this.

> 15. Define three more menu items at this level with labels of Next, Prev, and Last. Give them the accelerator values right arrow (`CURSOR-RIGHT`), left arrow (`CURSOR-LEFT`), and down arrow (`CURSOR-DOWN`), respectively:

> The double indentation identifies Navigation as a new submenu with four items as menu items under it.

> | To provide trigger blocks to execute when these items are selected:

> 1. Go into the Section Editor to define a `CHOOSE` trigger for menu item `m_No_FirstLast`.

> 2. Get to this trigger block in one of two ways:

> - Go into the Section Editor, select the Triggers section and then the menu item from the list of objects on the right.
> - Select the menu item in the design window, and then click the Edit Code button.

> As you can see, the menu is active and its menu items are selectable even in design mode.

> 3. To duplicate the effect of the radio set button with the same label, copy its `ASSIGN` statement into the new trigger:

>     DO:
>       ASSIGN BtnFirst:SENSITIVE IN FRAME CustQuery = NO
>              BtnLast:SENSITIVE  IN FRAME CustQuery = NO.
>     END.

> 4. Make one change to the statement to qualify each button with the name of the frame it’s in.

> Why is this necessary here and not in the trigger block for the radio set? Both the buttons and the radio set are in the frame called CustQuery, so Progress identifies the frame for the buttons by default. The menu, however, is not part of the frame, but rather is owned by the window. For this reason Progress requires that you identify the frame where the buttons are located.

> This is the `CHOOSE` trigger for the m_No_Prev item:

>     DO:
>         BtnPrev:SENSITIVE IN FRAME CustQuery = NO.
>     END.

> The next code block is the trigger for the m_Allow_All item.

> 5. So that you don’t have to type IN FRAME CustQuery three times, make it the default frame for an entire `DO` block:

>     DO:
>       DO WITH FRAME CustQuery:
>            ASSIGN BtnFirst:SENSITIVE = YES
>                 BtnLast:SENSITIVE = YES
>                 BtnPrev:SENSITIVE = YES.
>       END.
>     END.

> 6. Define trigger actions for the four Navigation menu items. These triggers duplicate the actions of the `CHOOSE` triggers for the four buttons with the same labels. Often you will want some of your menu items to duplicate the action of toolbar buttons. In general, it is considered appropriate user interface design to provide a menu item for every toolbar button, such that the toolbar buttons duplicate the most frequently used menu items. One way to do this without duplicating code is to apply the `CHOOSE` event to the button from the menu item, as in this trigger block for the `m_First menu` item:

>     DO:
>         APPLY "CHOOSE" TO btnFirst IN FRAME CustQuery.
>     END.

> A cleaner way to do this, however, is to avoid defining a `CHOOSE` trigger for the menu item at all. Instead, overload the trigger definition for the button by including the menu item name in the `ON` phrase, as in this modified trigger block for `btnNext`:

> 7. Make similar changes to the `btnPrev` and `btnLast` triggers to enable their actions for the same menu items as well.

> 8. Run the window and try out all the buttons and accelerators, including the nested items under the Navigation submenu:

> | To complete your menu bar:

> 1. Go back into its property sheet. You can do this by selecting any item in the menu in the design window and clicking the Object Properties button.

> 2. Add a top-level submenu called Show, with a menu item of Warehouse Info. Check on the Toggle-box toggle box to make this a menu item that represents a logical value.

> 3. Add a final top-level submenu called Help, with a menu item called Help About.

> The property sheet should look like this when you’re done:

> The Warehouse Info item is selected to show that the toggle box is checked.

> 4. Define a trigger for the Warehouse Info item:

>     DO:
>       DO WITH FRAME CustQuery:
>           IF SELF:CHECKED THEN
>               ASSIGN lShowWarehouse:SCREEN-VALUE = "YES"
>               cWorstWH:HIDDEN = NO
>                      cBestWH:HIDDEN = NO.
>           ELSE
>               ASSIGN lShowWarehouse:SCREEN-VALUE = "NO"
>                      cWorstWH:HIDDEN = YES
>                      cBestWH:HIDDEN = YES.
>       END.
>     END.

> Because it is a toggle box item, it has a `VALUE-CHANGED` trigger rather than a `CHOOSE` trigger.

> This duplicates the code for the Warehouse Info toggle box in the window, but also sets the screen value of that toggle so that the two are kept in sync. Remember that the `SELF` keyword within a trigger block always refers to the object handle that invoked the trigger. The `CHECKED` attribute tells you whether the menu item is currently checked or not. Each time you select this menu item, the check box alternately appears and disappears, and the value of `CHECKED` reverses between true and false.

> As with earlier triggers, you have to enclose all of this in a `DO WITH FRAME CustQuery` block to identify all these objects.

> ##### Using the MENU-DROP event

> Just as this trigger block changes the displayed value for the toggle box called lShowWarehouse when the corresponding menu item is selected, make sure that the initial value of the menu item matches the current value of the toggle box when you drop down the Show menu. There is a `MENU-DROP` event for submenus (and for pop-up menus) to let you intercept this event and execute whatever code you need before the menu items under the submenu or the pop-up menu are displayed. In this case, you use the event to initialize the `CHECKED` attribute of the `m_Warehouse_Info` item to match the toggle box. In other cases, you could use this event to disable menu items that aren’t currently applicable or even to add additional items to the menu dynamically. You’ll learn how to create dynamic menus and to make changes to menus dynamically in Chapter 18, " Using Dynamic Graphical Objects."

> This is the `MENU-DROP` event for the `m_Show` submenu:

>     DO:
>       MENU-ITEM m_Warehouse_Info:CHECKED IN MENU m_CustWin =
>           LOGICAL (lShowWarehouse:SCREEN-VALUE IN FRAME CustQuery).
>     END.

> All the required qualifiers make this statement a bit more complex than it otherwise might be:

> - You have to identify `m_Warehouse_Info` as a `MENU-ITEM`.

> - You have to qualify the item as being `IN MENU m_CustWin`.

> - You have to identify the `lShowWarehouse` toggle box as being `IN FRAME CustQuery`.

> ##### Using OWNER, PARENT, FIRST-CHILD, and NEXT-SIBLING

> In this section, you complete the menu item for the sample window.

> | To define a `CHOOSE` trigger for the Help About item:

>     DO:
>         DEFINE VARIABLE cChildren AS CHARACTER  NO-UNDO.
>         DEFINE VARIABLE hChild    AS HANDLE     NO-UNDO.
>         hChild = SUB-MENU m_Navigation:FIRST-CHILD IN MENU m_CustWin.
>         DO WHILE VALID-HANDLE(hChild):
>             cChildren = cChildren + hChild:LABEL + ",".
>             hChild = hChild:NEXT-SIBLING.
>         END.

>         MESSAGE "Here's some information about this menu:" SKIP
>           "The menu's owner is " MENU m_CustWin:OWNER:TITLE SKIP
>           "This item's label is "
>               MENU-ITEM m_Help_About:LABEL IN MENU m_CustWin SKIP
>           "This item's parent object is "
>               MENU-ITEM m_Help_About:PARENT:LABEL IN MENU m_CustWin SKIP
>           "The Navigation submenu's children are " cChildren.
>     END.

> This displays some information about the menu to show you this information:

> - The menu itself has the window as an `OWNER`, not a `PARENT`.

> - Each menu item has attributes, such as its `LABEL`, that you can query and set as you can for other objects.

> - Each menu item and submenu has a `PARENT` attribute that leads you up through the menu hierarchy.

> - Each parent object has a `FIRST-CHILD` attribute. Using that and the `NEXT-SIBLING` attribute of each child, you can identify all the objects at any level in the menu tree.

> Figure 9–3 shows what you see when you run the window and select Help Help About.

> Figure 9–3: Sample menu information message

> ##### Looking at the code

> Now look at the statements the AppBuilder generated when you built the menu. You can also see this code in the Code Preview window.

> First the code defines each submenu:

>     /* Menu Definitions                                                     */
>     DEFINE SUB-MENU m_Navigation
>            MENU-ITEM m_First       LABEL "First"      ACCELERATOR "CURSOR-UP"
>            MENU-ITEM m_Next        LABEL "Next"       ACCELERATOR "CURSOR-RIGHT"
>            MENU-ITEM m_Prev        LABEL "Prev"       ACCELERATOR "CURSOR-LEFT"
>            MENU-ITEM m_Last        LABEL "Last"       ACCELERATOR "CURSOR-DOWN".
>     DEFINE SUB-MENU m_Buttons
>            MENU-ITEM m_No_FirstLast LABEL "No &First/Last"
>            MENU-ITEM m_No_Prev      LABEL "No &Prev"
>            MENU-ITEM m_Allow_All    LABEL "Allow &All"
>            RULE
>            SUB-MENU  m_Navigation   LABEL "Navigation"    .
>     DEFINE SUB-MENU m_Show
>            MENU-ITEM m_Warehouse_Info LABEL "Warehouse Info" TOGGLE-BOX.
>     DEFINE SUB-MENU m_Help
>            MENU-ITEM m_Help_About   LABEL "Help About"    .

> Once all the submenus are defined, the code defines the menu bar itself:

>     DEFINE MENU m_CustWin MENUBAR
>           SUB-MENU  m_Buttons      LABEL "Buttons"
>           SUB-MENU  m_Show         LABEL "Show"
>           SUB-MENU  m_Help         LABEL "Help"         .

> After the window is defined, the menu bar is attached to the window:

>     ASSIGN {&WINDOW-NAME}:MENUBAR     = MENU m_CustWin:HANDLE.

Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095