![]() |
Introduction to PM ProgrammingWritten by Larry Salomon Jr. |
IntroductionThe 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, 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.
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.
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.
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.
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.
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.
This message is sent when a menu or one of its pull-downs has finished the
selection process.
Regardless of when you decide to load the menu, you will have to use
WinLoadMenu() to load the menu template.
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:
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.
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.
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.'
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.
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!
|