Introduction to PM Programming - Jun 1995

From EDM2
Jump to: navigation, search

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. <grin>

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.

Last Month

Last month we continued our look at the menu control. This month, we will continue by looking at a sample application that uses a menu control.

Feedback

I received a letter or two asking about the pullright menus that the Workplace Shell uses which have the buttons to the right of the text. Be patient - I will get to that shortly! <grin>

MENU - The Sample

Our promised sample is quite a trivial application. In fact, it does absolutely nothing except demonstrate some (but not all) of the features of the menu class. The complete source can be found in [MENU.ZIP].

There are four points of interest in the application: the MENU template (MENU.RC), the WM_INITMENU processing (MENU.C), the WM_COMMAND processing (MENU.C), and a small part of main() (MENU.C). Before you look at these sections, run the application to become familiar with its operation; this will enable you to better understand the effects of certain things within this landmarks.

  1. See the bitmapped "Exit" menu item.
  2. See the aligned text to the right of the menu items under the "File" and "Edit" pulldowns.
  3. See how the menu items under "Help" are all disabled.
  4. See the separators in the "File" pulldown.
  5. See the pullright in the "File" pulldown.
  6. See how "Undo" changes to "Redo" when you select it (and how "Redo" changes to "Undo" when it is selected).
  7. See how the application exits when you select "Exit".

The MENU Template

Take a look at the menu template below:

#include <os2.h>
#include "rc.h"

ICON RES_CLIENT MENU.ICO
BITMAP 1024 MENUITEM.BMP

MENU RES_CLIENT
{
   SUBMENU "~File", M_FILE
   {
	MENUITEM "~New\tCtrl+N", MI_NEW
	MENUITEM "~Open...\tCtrl+O", MI_OPEN
	MENUITEM "~Save\tCtrl+S", MI_SAVE
	MENUITEM "Save ~as...", MI_SAVEAS
	MENUITEM "~Close", MI_CLOSE
	MENUITEM SEPARATOR
	SUBMENU "~Import", M_IMPORT
	{
	   MENUITEM "~Text...", MI_IMPTEXT
	   MENUITEM "~Picture...", MI_IMPPICTURE
	}
	MENUITEM SEPARATOR
	MENUITEM "#1024", SC_CLOSE, MIS_BITMAP | MIS_SYSCOMMAND
   }
   SUBMENU "~Edit", M_EDIT
   {
	MENUITEM "", MI_UNDO
	MENUITEM "~Copy\tCtrl+Ins", MI_COPY
	MENUITEM "C~ut\tShift+Del", MI_CUT
	MENUITEM "~Paste\tShift+Ins", MI_PASTE
   }
   SUBMENU "~Help", M_HELP
   {
	MENUITEM "Help ~index...", MI_HELPINDEX, , MIA_DISABLED
	MENUITEM "~General help...", MI_GENERALHELP, , MIA_DISABLED
	MENUITEM "~Using help...", MI_USINGHELP, , MIA_DISABLED
	MENUITEM "~Keys help...", MI_KEYSHELP, , MIA_DISABLED
   }
}

The bitmapped menu item in number 1 above is accomplished by doing three things:

  • The bitmap resource is made part of the .RC file.
  • The menu item text is of the form "#nnnnn" where "nnnnn" is the ASCII representation of the resource identifier of the bitmap.
  • The menu item is given the style MIS_BITMAP.

You should be cautioned against using bitmaps in menu items. Unless the bitmaps have no text in them at all, they should not be used because translating bitmaps to other languages involves much more work than simply translating text. We obviously did not follow our own advice.

To achieve the effect in number 2 above, the text to be "tabbed" to the right is preceded by the text "\t". This is indeed telling the menu control to tab the following text, aligning it as you would expect. Unlike a real tab, however, there is no predefined amount of space. All text that is tabbed is moved to the right until it is possible to align all of it.

Item 3 is achieved by defining the MENUITEMs with the attribute MIA_DISABLED. It is normally considered better practice to do this dynamically in the WM_INITMENU processing (see below), but I wanted to demonstrate the use of menuitem attributes (MIA_) in the .RC file.

The separators after "Close" and before "Exit" (item 4) are created using the special form of the MENUITEM statement.

   MENUITEM SEPARATOR

This is a form of the more general statement:

   MENUITEM "", -1, MIS_SEPARATOR

Since separators are used frequently, the shorter form was provided to save you some typing.

Item 5 demonstrates what happens when you use the SUBMENU statement within a SUBMENU block. If another SUBMENU statement is included within this one, another pullright is built. Note that the SUBMENU statement is shorthand for:

   MENUITEM "text", id, MIS_SUBMENU

Menu Messages

To understand item 6, we need to look at the WM_INITMENU processing.

case WM_INITMENU:
   switch (SHORT1FROMMP(mpParm1)) {
   case M_EDIT:
	{
	   //-------------------------------------------------------------
	   // Set the Edit pulldown items to the correct state
	   //-------------------------------------------------------------
	   CHAR achText[32];
	   MENUITEM miItem;

	   if (pidData->bUndo) {
		strcpy(achText,"~Undo");
	   } else {
		strcpy(achText,"~Redo");
	   } /* endif */

	   WinSendMsg(WinWindowFromID(pidData->hwndFrame,FID_MENU),
			  MM_QUERYITEM,
			  MPFROM2SHORT(M_EDIT,TRUE),
			  MPFROMP(&miItem));

	   WinSendMsg(miItem.hwndSubMenu,
			  MM_SETITEMTEXT,
			  MPFROMSHORT(MI_UNDO),
			  MPFROMP(achText));
	}
	break;
   default:
	return WinDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
   } /* endswitch */
   break;

The WM_INITMENU message is sent to the owner of the menu just before the top-level menu or before a submenu is displayed. If the owner is a frame window, the message is rerouted to the client window to allow it to change the menu to match the current state of the application. The parameters of the message are listed below:

  • SHORT1FROMMP(mpParm1) - identifier of the menu. For the top level menu, this is FID_MENU.
  • HWNDFROMMP(mpParm2) - window handle of the menu about to be displayed.

Typically, this message is used in conjunction with the MM_SETITEMATTR message to change the attributes of one or more menuitems and the MM_SETITEMTEXT message to change the menuitem text.

The MM_SETITEMATTR takes the following parameters:

  • MPFROM2SHORT(usId,bSubmenus) - usId specifies the identifier of the menuitem to act upon and bSubmenus is TRUE if submenus of the menu receiving this message should be searched to find the menuitem.
  • MPFROM2SHORT(usMask,usValues) - usMask specifies one or more MIA_ values which will be affected and the corresponding bits in usValues specify the new values of the attributes specified in the mask. All bits in usValues that are not set in usMask are ignored.

The MM_QUERYITEMATTR message is the reverse of the MM_SETITEMATTR message.

  • MPFROM2SHORT(usId,bSubmenus) - usId specifies the identifier of the menuitem to act upon and bSubmenus is TRUE if submenus of the menu receiving this message should be searched to find the menuitem.
  • MPFROMSHORT(usMask) - usMask specifies one or more MIA_ values which will be returned.
  • LONGFROMMR() - returns the settings of the bits in usMask.

For changing the text of a menu item, use the MM_SETITEMTEXT message.

  • MPFROMSHORT(usId) - usId specifies the identifier of the menuitem to act upon.
  • MPFROMP(pchText) - pchText points to the new text.

Note that there is no "search submenus" flag; this is to be consistent with the MM_QUERYITEMTEXT message which uses the second half of mpParm1 to specify the size of the receiving buffer. For the meantime, this means that you can only use this message in a WM_INITMENU message when the appropriate pulldown is the "object of your desire". We'll see how to use this message anytime at a later time.

As you read, the MM_QUERYITEMTEXT is sent to get the text of a menuitem.

  • MPFROM2SHORT(usId,usSzBuf) - usId specifies the identifier of the menuitem to act upon and usSzBuf specifies the size of the receiving buffer.
  • MPFROMP(pchText) - pchText points to the receiving buffer.
  • LONGFROMMR() - returns the length of the menuitem text.

Looking at the code, then, we can see that we are changing the text of the MI_UNDO menuitem according to the value of pcidData->bUndo.

Whenever the user selects a menuitem, the menu sends (to the frame, which then reroutes the message to the client) a WM_COMMAND message. This message was discussed in volume 2, issue 4, so we won't dissect it again. The code, however, is displayed below:

case WM_COMMAND:
   switch (SHORT1FROMMP(mpParm1)) {
   case MI_UNDO:
	pidData->bUndo=!pidData->bUndo;
	break;
   default:
	return WinDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
   } /* endswitch */
   break;

Notice that in the MI_UNDO section we toggle the value of pcidData->bUndo.

Finally, simply to reiterate a point discussed before, to create the menu, one simply needs to specify FCF_MENU in the frame-creation-flags (a pointer to which is passed as the third parameter to WinCreateStdWindow()) as shown below.

ulCreate=FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX | FCF_MENU | FCF_ICON |
		FCF_SIZEBORDER | FCF_TASKLIST | FCF_SHELLPOSITION;

hwndFrame=WinCreateStdWindow(HWND_DESKTOP,
				     WS_VISIBLE,
				     &ulCreate,
				     CLS_CLIENT,
				     "Menu sample",
				     0,
				     NULLHANDLE,
				     RES_CLIENT,
				     &hwndClient);

Conclusion

That wraps it up for the discussion of "traditional" menus. You might wonder why many of the features of the menu class were left out. Consider it my way of discouraging their use. In fact, because CUA '91 (which superceded CUA '89) no longer uses these "traditional" menus, you shouldn't be using these types of menus at all, except for quick applications when you don't care about CUA compliance.

Next month, we will finish the menu control by looking at popup menus and other things necessary to use menus within your applications. As always, any feedback about this column would be greatly appreciated.