Jump to content

Introduction to PM Programming - Jun 1995: Difference between revisions

From EDM2
Prokushev (talk | contribs)
No edit summary
 
Ak120 (talk | contribs)
mNo edit summary
 
(5 intermediate revisions by 2 users not shown)
Line 1: Line 1:
Written by [[Larry Salomon Jr.]]
''Written by [[Larry Salomon Jr.]]''


<h2>Introduction</h2>
==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.


<p>The purpose of this column is to provide the readers out there who are not
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>
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.


<p>I will gladly entertain feedback from the readers about what was &quot;glossed
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.
over&quot; 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. &lt;grin&gt;


<p>It should be said that you must not depend solely on this column to teach
==Last Month==
you how to develop PM applications; instead, this should be viewed as a
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.
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.


<h2>Last Month</h2>
==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>


<p>Last month we continued our look at the menu control.  This month, we will
==MENU - The Sample==
continue by looking at a sample application that uses a menu control.
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].


<h2>Feedback</h2>
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.


<p>I received a letter or two asking about the pullright menus that the
#See the bitmapped "Exit" menu item.
Workplace Shell uses which have the buttons to the right of the text. Be
#See the aligned text to the right of the menu items under the "File" and "Edit" pulldowns.
patient - I will get to that shortly! &lt;grin&gt;
#See how the menu items under "Help" are all disabled.
#See the separators in the "File" pulldown.
#See the pullright in the "File" pulldown.
#See how "Undo" changes to "Redo" when you select it (and how "Redo" changes to "Undo" when it is selected).
#See how the application exits when you select "Exit".


<h2>MENU - The Sample</h2>
==The MENU Template==
 
Take a look at the menu template below:
<p>Our promised sample is quite a trivial application.  In fact, it does
<pre>
absolutely nothing except demonstrate some (but not all) of the features of
#include <os2.h>
the menu class.  The complete source can be found in <a HREF="menu">
#include "rc.h"
MENU.ZIP</a>.
 
<p>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.
 
<ol>
<li>See the bitmapped &quot;Exit&quot; menu item.
 
<li>See the aligned text to the right of the menu items under the &quot;File&quot;
and &quot;Edit&quot; pulldowns.
<li>See how the menu items under &quot;Help&quot; are all disabled.
<li>See the separators in the &quot;File&quot; pulldown.
<li>See the pullright in the &quot;File&quot; pulldown.
 
<li>See how &quot;Undo&quot; changes to &quot;Redo&quot; when you select it (and how &quot;Redo&quot;
changes to &quot;Undo&quot; when it is selected).
<li>See how the application exits when you select &quot;Exit&quot;.
</ol>
 
<h2>The MENU Template</h2>
 
<p>Take a look at the menu template below:
 
<pre><small>
&#35;include &lt;os2.h&gt;
&#35;include &quot;rc.h&quot;


ICON RES_CLIENT MENU.ICO
ICON RES_CLIENT MENU.ICO
Line 76: Line 39:
MENU RES_CLIENT
MENU RES_CLIENT
{
{
   SUBMENU &quot;~File&quot;, M_FILE
   SUBMENU "~File", M_FILE
   {
   {
MENUITEM &quot;~New\tCtrl+N&quot;, MI_NEW
MENUITEM "~New\tCtrl+N", MI_NEW
MENUITEM &quot;~Open...\tCtrl+O&quot;, MI_OPEN
MENUITEM "~Open...\tCtrl+O", MI_OPEN
MENUITEM &quot;~Save\tCtrl+S&quot;, MI_SAVE
MENUITEM "~Save\tCtrl+S", MI_SAVE
MENUITEM &quot;Save ~as...&quot;, MI_SAVEAS
MENUITEM "Save ~as...", MI_SAVEAS
MENUITEM &quot;~Close&quot;, MI_CLOSE
MENUITEM "~Close", MI_CLOSE
MENUITEM SEPARATOR
MENUITEM SEPARATOR
SUBMENU &quot;~Import&quot;, M_IMPORT
SUBMENU "~Import", M_IMPORT
{
{
  MENUITEM &quot;~Text...&quot;, MI_IMPTEXT
  MENUITEM "~Text...", MI_IMPTEXT
  MENUITEM &quot;~Picture...&quot;, MI_IMPPICTURE
  MENUITEM "~Picture...", MI_IMPPICTURE
}
}
MENUITEM SEPARATOR
MENUITEM SEPARATOR
MENUITEM &quot;&#35;1024&quot;, SC_CLOSE, MIS_BITMAP | MIS_SYSCOMMAND
MENUITEM "#1024", SC_CLOSE, MIS_BITMAP | MIS_SYSCOMMAND
   }
   }
   SUBMENU &quot;~Edit&quot;, M_EDIT
   SUBMENU "~Edit", M_EDIT
   {
   {
MENUITEM &quot;&quot;, MI_UNDO
MENUITEM "", MI_UNDO
MENUITEM &quot;~Copy\tCtrl+Ins&quot;, MI_COPY
MENUITEM "~Copy\tCtrl+Ins", MI_COPY
MENUITEM &quot;C~ut\tShift+Del&quot;, MI_CUT
MENUITEM "C~ut\tShift+Del", MI_CUT
MENUITEM &quot;~Paste\tShift+Ins&quot;, MI_PASTE
MENUITEM "~Paste\tShift+Ins", MI_PASTE
   }
   }
   SUBMENU &quot;~Help&quot;, M_HELP
   SUBMENU "~Help", M_HELP
   {
   {
MENUITEM &quot;Help ~index...&quot;, MI_HELPINDEX, , MIA_DISABLED
MENUITEM "Help ~index...", MI_HELPINDEX, , MIA_DISABLED
MENUITEM &quot;~General help...&quot;, MI_GENERALHELP, , MIA_DISABLED
MENUITEM "~General help...", MI_GENERALHELP, , MIA_DISABLED
MENUITEM &quot;~Using help...&quot;, MI_USINGHELP, , MIA_DISABLED
MENUITEM "~Using help...", MI_USINGHELP, , MIA_DISABLED
MENUITEM &quot;~Keys help...&quot;, MI_KEYSHELP, , MIA_DISABLED
MENUITEM "~Keys help...", MI_KEYSHELP, , MIA_DISABLED
   }
   }
}
}
</pre>
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.


</small></pre>
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.
 
<p>The bitmapped menu item in number 1 above is accomplished by doing three
things:
 
<ul>
<li>The bitmap resource is made part of the .RC file.
<li>The menu item text is of the form &quot;&#35;nnnnn&quot; where &quot;nnnnn&quot; is the ASCII
representation of the resource identifier of the bitmap.
<li>The menu item is given the style MIS_BITMAP.
</ul>


<p>You should be cautioned against using bitmaps in menu items.  Unless the
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.
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.


<p>To achieve the effect in number 2 above, the text to be &quot;tabbed&quot; to the
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.
right is preceded by the text &quot;\t&quot;. 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.


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


<p>Item 3 is achieved by defining the MENUITEMs with the attribute
This is a form of the more general statement:
MIA_DISABLED.  It is normally considered better practice to do this
    MENUITEM "", -1, MIS_SEPARATOR
dynamically in the WM_INITMENU processing (see below), but I wanted to
demonstrate the use of menuitem attributes (MIA_) in the .RC file.


<p>The separators after &quot;Close&quot; and before &quot;Exit&quot; (item 4) are created using
Since separators are used frequently, the shorter form was provided to save you some typing.
the special form of the MENUITEM statement.


<pre><small>
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 pull right is built. Note that the SUBMENU statement is shorthand for:
    MENUITEM SEPARATOR
    MENUITEM "text", id, MIS_SUBMENU
</small></pre>


<p>This is a form of the more general statement:
==Menu Messages==
 
To understand item 6, we need to look at the WM_INITMENU processing.  
<pre><small>
<pre>
    MENUITEM &quot;&quot;, -1, MIS_SEPARATOR
 
</small></pre>
 
<p>Since separators are used frequently, the shorter form was provided to save
you some typing.
 
<p>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:
 
<pre><small>
    MENUITEM &quot;text&quot;, id, MIS_SUBMENU
</small></pre>
 
<h2>Menu Messages</h2>
 
<p>To understand item 6, we need to look at the WM_INITMENU processing.
 
<pre><small>
case WM_INITMENU:
case WM_INITMENU:
   switch (SHORT1FROMMP(mpParm1)) {
   switch (SHORT1FROMMP(mpParm1)) {
Line 178: Line 106:
  MENUITEM miItem;
  MENUITEM miItem;


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


  WinSendMsg(WinWindowFromID(pidData-&gt;hwndFrame,FID_MENU),
  WinSendMsg(WinWindowFromID(pidData->hwndFrame,FID_MENU),
  MM_QUERYITEM,
  MM_QUERYITEM,
  MPFROM2SHORT(M_EDIT,TRUE),
  MPFROM2SHORT(M_EDIT,TRUE),
Line 199: Line 127:
   } /* endswitch */
   } /* endswitch */
   break;
   break;
</pre>
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.


</small></pre>
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.


<p>The WM_INITMENU message is sent to the owner of the menu just before the
The MM_SETITEMATTR takes the following parameters:
top-level menu or before a submenu is displayed.  If the owner is a frame
*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.
window, the message is rerouted to the client window to allow it to change
*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 menu to match the current state of the application.  The parameters of
the message are listed below:
 
<ul>
<li>SHORT1FROMMP(mpParm1) - identifier of the menu.  For the top level menu,
this is FID_MENU.
<li>HWNDFROMMP(mpParm2) - window handle of the menu about to be displayed.
</ul>
 
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.
 
<p>The MM_SETITEMATTR takes the following parameters:
 
<ul>
<li>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.
<li>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.
</ul>


The MM_QUERYITEMATTR message is the reverse of the MM_SETITEMATTR message.
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.
<ul>
*MPFROMSHORT(usMask) - usMask specifies one or more MIA_ values which will be returned.
 
*LONGFROMMR() - returns the settings of the bits in usMask.
<li>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.
<li>MPFROMSHORT(usMask) - usMask specifies one or more MIA_ values which will
be returned.
<li>LONGFROMMR() - returns the settings of the bits in usMask.
</ul>
 
For changing the text of a menu item, use the MM_SETITEMTEXT message.
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 any time at a later time.


<ul>
As you read, the MM_QUERYITEMTEXT is sent to get the text of a menuitem.
<li>MPFROMSHORT(usId) - usId specifies the identifier of the menuitem to act
*MPFROM2SHORT(usId,usSzBuf) - usId specifies the identifier of the menuitem to act upon and usSzBuf specifies the size of the receiving buffer.
upon.
*MPFROMP(pchText) - pchText points to the receiving buffer.
<li>MPFROMP(pchText) - pchText points to the new text.
*LONGFROMMR() - returns the length of the menuitem text.
</ul>
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.
 
Note that there is no &quot;search submenus&quot; 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 &quot;object of your desire&quot;. We'll see how to use
this message anytime at a later time.


 
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:
<p>As you read, the MM_QUERYITEMTEXT is sent to get the text of a menuitem.
<pre>
 
<ul>
<li>MPFROM2SHORT(usId,usSzBuf) - usId specifies the identifier of the
menuitem to act upon and usSzBuf specifies the size of the receiving
buffer.
<li>MPFROMP(pchText) - pchText points to the receiving buffer.
<li>LONGFROMMR() - returns the length of the menuitem text.
</ul>
 
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.
 
<p>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:
 
<pre><small>
case WM_COMMAND:
case WM_COMMAND:
   switch (SHORT1FROMMP(mpParm1)) {
   switch (SHORT1FROMMP(mpParm1)) {
Line 286: Line 164:
   } /* endswitch */
   } /* endswitch */
   break;
   break;
</small></pre>
</pre>
 
Notice that in the MI_UNDO section we toggle the value of pcidData->bUndo.
<p>Notice that in the MI_UNDO section we toggle the value of pcidData-&gt;bUndo.
 
<p>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.
 
<pre><small>


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.
<pre>
ulCreate=FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX | FCF_MENU | FCF_ICON |
ulCreate=FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX | FCF_MENU | FCF_ICON |
FCF_SIZEBORDER | FCF_TASKLIST | FCF_SHELLPOSITION;
FCF_SIZEBORDER | FCF_TASKLIST | FCF_SHELLPOSITION;
Line 304: Line 176:
    &ulCreate,
    &ulCreate,
    CLS_CLIENT,
    CLS_CLIENT,
    &quot;Menu sample&quot;,
    "Menu sample",
    0,
    0,
    NULLHANDLE,
    NULLHANDLE,
    RES_CLIENT,
    RES_CLIENT,
    &hwndClient);
    &hwndClient);
</small></pre>
</pre>
 
<h2>Conclusion</h2>


<p>That wraps it up for the discussion of &quot;traditional&quot; menus. You might
==Conclusion==
wonder why many of the features of the menu class were left out. Consider
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 superseded 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.
it my way of discouraging their use. In fact, because CUA '91 (which
superceded CUA '89) no longer uses these &quot;traditional&quot; 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.


<p>Next month, we will finish the menu control by looking at popup menus and
[[Category: PM Articles]]
other things necessary to use menus within your applications.  As always,
any feedback about this column would be greatly appreciated.

Latest revision as of 23:07, 10 October 2022

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 pull right 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 any time 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 superseded 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.