Feedback Search Top Backward Forward
EDM/2

Introduction to PM Programming

Written by Larry Salomon Jr.

 

Introduction

The purpose of this column is to provide the readers out there who are not familiar with PM application development the information necessary to satisfy their curiosity, educate themselves, and give them an advantage over the documentation supplied by IBM. Of course, much of this stuff could probably be found in one of the many books out there, but the problem with books in general is that they don't answer the questions you have after you read the book the first time through.

I will gladly entertain feedback from the readers about what was `glossed over' or what was detailed well, what tangential topics need to be covered and what superfluous crap should have been removed. This feedback is essential in guaranteeing that you get what you pay for.

It should be said that you must not depend solely on this column to teach you how to develop PM applications; instead, this should be viewed as a supplement to your other information storehouses (books, the network conferences, etc.). Because this column must take a general approach, there will be some topics that you would like to see discussed that really do not belong here. Specific questions can be directed to me via email and I will do my best to answer them in a timely fashion.

This month

This month, we will finish up our tour of the menu window class by looking at how popup menus are implemented, more of the messages that are used for interacting with the menu control, and one or two other neat things that can be done with menus.

Popup Menus

I can remember when my colleague at IBM, Thierry Samama, wrote a program called UBU, which sported a popup menu to avoid using real estate which was quite precious. These behaved much like their modern counterparts and I wouldn't be surprised to find that IBM's CUA group lifted the concept from this program which was developed when OS/2 1.2 was the current product level.

popup.gif
Figure 1) Simple popup menu

From a user's perspective, popup menus are requested by pressing some combination of mouse buttons or (since CUA requires that all functions are accessible by mouse and keyboard) keystrokes. The user likes popup menus because they do not take up screen space that could be used for the workarea(s) and because they can be made context-sensitive (which is a fancy word to mean that only menuitems that are valid for the object(s) under the location of the mouse are displayed).

From a developer's perspective, popup menu requests are sent as a single message to the control under the mouse. It is the task of the window procedure associated with the control to determine what to do - if anything - with the request. The programmer hates popup menus because they create more work for them. However, the results are very nice, so the extra effort is worth it (in my opinion).

With the exception of the container control (WC_CONTAINER), the standard window classes ignore the popup menu request. Thus, you will have to subclass the controls where you want to receive this message. Once you receive the message, the remainder of the processing is trivial - there is only one function call to be added and the WM_COMMAND message processing remains unchanged.

The popup menu request message is WM_CONTEXTMENU. The system fortunately handles both the mouse and keyboard combinations for you and also uses the current settings in the `Mappings' tab of the Mouse settings notebook.

WM_CONTEXTMENU

  • SHORT1FROMMP(mpParm1) - x position of the mouse in window coordinates
  • SHORT2FROMMP(mpParm2) - y position of the mouse in window coordinates
  • SHORT1FROMMP(mpParm1) - reserved
  • SHORT2FROMMP(mpParm2) - TRUE if the keyboard was used, FALSE otherwise
  • SHORT1FROMMR(mrResult) - TRUE if the message was processed

Remember, window coordinates are relative to the lower left corner of the window to which they are associated. This isn't what we want, as we'll see below.

Once you receive the message and have decided to display a popup menu, you will call the function WinPopupMenu() to perform all of the work. It will convert the menu from horizontal to vertical format and will display the menu at the specified location.


BOOL WinPopupMenu(hwndParent,
			hwndOwner,
			hwndMenu,
			ulX,
			ulY,
			ulItem,
			ulFlags);
Figure 2) WinPopupMenu() function

hwndParent and hwndOwner are the parent and owner windows of the menu. hwndMenu is the handle of the menu to be displayed (see below). ulX and ulY are the desktop coordinates at which the menu is to be displayed. ulItem is the initial item to be under the mouse and is ignored unless PU_POSITIONONITEM or PU_SELECTITEM is specified in ulFlags. ulFlags is a combination of flags the controls how the function behaves.

Flag Description
PU_POSITIONONITEM Specifies the initial item under the mouse
PU_HCONSTRAIN Insures that the menu is within the left and right boundaries of the display
PU_VCONSTRAIN Insures that the menu is within the top and bottom boundaries of the display
PU_NONE Specifies that the keyboard was used to invoke the menu
PU_MOUSEBUTTON1DOWN Specifies that mouse button 1 was used to invoke the menu
PU_MOUSEBUTTON2DOWN Specifies that mouse button 2 was used to invoke the menu
PU_MOUSEBUTTON3DOWN Specifies that mouse button 3 was used to invoke the menu
PU_SELECTITEM Specifies that ulItem is to be selected
PU_MOUSEBUTTON1 Specifies that mouse button 1 can be used to select a menuitem
PU_MOUSEBUTTON2 Specifies that mouse button 2 can be used to select a menuitem
PU_MOUSEBUTTON3 Specifies that mouse button 3 can be used to select a menuitem
PU_KEYBOARD Specifies that the keyboard can be used to select a menuitem

Since WM_CONTEXTMENU gives you window coordinates and WinPopupMenu() expects desktop coordinates, you will either have to call WinMapWindowPoints() to convert from one to the other or call WinQueryPointerPos() to simply retrieve the desktop coordinates of the mouse.

From where do you get the value for hwndMenu? You have two options: you can load it just prior to calling WinPopupMenu() or you can load it during the WM_CREATE processing and save the value in a structure pointed to by your window words. Using the latter is acceptable; however, popup menus are usually a frequently used resource, so performance might suffer slightly if you load and destroy the menu everytime it is requested. This is especially evident if you are running the application from a network- mapped drive. If you still decide to do this, you can destroy the menu using the WinDestroyWindow() function when you receive the WM_MENUEND message.

WM_MENUEND

This message is sent when a menu or one of its pull-downs has finished the selection process.

  • SHORT1FROMMP(mpParm1) - specifies the identifier of the menu who is finished the selection process.
  • HWNDFROMMP(mpParm2) - specifies the handle of the menu.

Regardless of when you decide to load the menu, you will have to use WinLoadMenu() to load the menu template.


WinLoadMenu(hwndFrame,hmodDll,ulResourceId);
Figure 3) WinLoadMenu() function

hwndFrame is the handle of the frame to which the menu is to be attached. In the `old' days, when menus were simply action bars, you would indeed specify the frame window handle here, and the function would automatically assign the window identifier FID_MENU to the loaded menu, attach it to the frame, and `reflow' the frame window. Since we have a popup menu - which we don't want to be displayed - we will use HWND_OBJECT here. hmodDll is the module handle containing the menu template. ulResourceId is the menu template identifier. Menu Messages

We now need to look at more of the messages that we need to interact with the control. The MM_QUERYITEM and MM_SETITEM messages are used to query information about and set information for a node in the menu tree, whether it is a branch or leaf node. Both messages have the same parameters:

  • SHORT1FROMMP(mpParm1) specifies the identifier of the node to search for
  • SHORT2FROMMP(mpParm1) specifies whether or not submenus should be searched also
  • PVOIDFROMMP(mpParm2) points to a MENUITEM structure.

typedef struct {
     SHORT iPosition;
     USHORT afStyle;
     USHORT afAttribute;
     USHORT id;
     HWND hwndSubMenu;
     ULONG hItem;
} MENUITEM,*PMENUITEM;
Figure 4) MENUITEM structure

iPosition is the 0-based position of the item within its parent. afStyle and afAttribute are the MIS_ and MIA_ flags associated with the item. id is the numeric identifier of the item. hwndSubMenu is the handle of the submenu associated with this item if the MIS_SUBMENU flag is specified in the afStyle field. hItem is a 4-byte application-usable area that is associated with the item.

As I stated in issue 3-6, a few people had asked about the buttons that are to the right of certain menuitems. The submenus associated with these are said to be a conditionally cascading submenu - the word `conditionally' comes from the behavior of the submenu, which is conditional on how the user interacts with it. If the user simply clicks on the text that contains the submenu, the default item is sent to the application via the WM_COMMAND message.

Although an MIS_CONDITIONALCASCADE style exists, the resource compiler (when I last checked) did not recognize this as a valid flag. So, this is added to the list of things that needs to be done to implement this user interface feature.

  • Set the style of the submenu to include MIS_CONDITIONALCASCADE
  • Set the default menuitem
  • Check the default menuitem for user-feedback

The first item can only be done using the MM_QUERYITEM and MM_SETITEM messags. Since this is a fairly frequently needed function, I created the setCascadeDefault() function, which is included in cascade.zip.

From CUA 89 to CUA 91 Style Menus

I have included with this month's column the MENU sample, which has been `ported' to use popup menus instead of the old-style menus. In doing so, I kept track of the changes that were necessary to give you an idea of the things that need to be done to convert your application to the `new look.'

  • Remove FCF_MENU from the frame creation flags passed to WinCreateStdWindow().
  • Add hwndMenu to instance data structure.
  • Load the menu in WM_CREATE and store the handle in the instance data.
  • Add processing for WM_CONTEXTMENU (#define INCL_WININPUT for the message and #define INCL_WINPOINTERS for pointer stuff).
  • Change references of WinWindowFromID(pidData->hwndFrame,FID_MENU) to pidData->hwndMenu.

This list is by-no-means conclusive, but it shows you what I had to do so that you have a rough idea of what you will also need to do.

Conclusion

This concludes our look at the menu control. As always, questions and comments are welcome via my email address. Next month, we'll continue our journey, but I have no idea where it will take us at this time. Keep reading!