How do I? - Part 3

From EDM2
Jump to: navigation, search
How Do I? / Part
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19

by Eric Slaats

Hi and welcome to the next lesson on PM programming. In this column simple Presentation Manager programming problems and philosophies will be discussed. This Column is aimed at people who are interested in PM programming or are simply curious what makes PM programs tick. To understand this column a little programming experience (preferably in C++) is recommended.

Last month we looked at some messages that play an important role in window creation, the WM_CREATE and WM_SIZE messages. In this issue we will focus on something completely different. Since we're still in the process of creating a basic application, we will once again focus on the frame window.

In this issue we will discuss a way to add some basic controls like a menu to the frame window. The basic program we were working on last month will be used as starting point for these new explorations.

Be lazy!

This is actually one of the key principles I use in programming. It can also be interpreted as: "Don't do what can be done for you!". The people at IBM spent lots of time developing this OS and put a lot of thought in it. Why do it over?

If you're wondering what I'm talking about, let me give a simple example. Last month we did a small program that set up one button in a frame window. We did this by simply creating an instance of the button class. The only thing we did ourselves was dictate (or calculate) the size of the button. We could have gone the difficult way and drawn the complete button ourselves; this way we would end up in a mess of graphic functions. The button class that is defined in OS/2 does all that for us.

Besides being lazy, I'm also a fan of the KISS principle (Keep It Simple Stupid). This month we will introduce resources as a possible way to keep it simple and be lazy.

Resources

Wouldn't it be great if we didn't have to program with these difficult C++ code lines, but simply draw our program on the screen and then generate it? Well, for a number of controls this is possible. We can define dialogs, and menus this way. Unfortunately, we still have to program the way the controls react to events. In future columns I will give a lot of attention to this way of programming; for now we will concentrate on menus.

But first let's describe what resources are.

In a resource file (.RC) we can describe controls with a scripting kind of language. This isn't exactly a programming language, but more a language that is used to describe the way things look. Some examples of resources that can be defined in resource files are:

  • Menus
  • Accelerator tables
  • Dialog and window templates
  • Icons
  • Fonts
  • Bit maps
  • Strings

In most cases, we don't have to bother to write the resource scripts ourselves. With most compilers, as well as with the OS/2 Toolkit, come dedicated resource editors. These tools offer a graphic environment in which you can paint the resources you'd like to use. (See also the description of Visual Age, Borland C++ and Watcom in issue #11).

Unfortunately, for the menu resource there usually isn't a graphical building tool. So let's see how a menu is created. The best way to illustrate this is to present an example:

MENU MAIN
BEGIN
        SUBMENU "Menu ~1", IDM_MENU1
        BEGIN
                MENUITEM "Item 1.~1", IDM_ITEM1_1
                MENUITEM "Item 1.~2", IDM_ITEM1_2
        END

        SUBMENU "Menu ~2", IDM_MENU2
        BEGIN
                MENUITEM "Item 2.~1", IDM_ITEM2_1
                MENUITEM SEPARATOR
                MENUITEM "Item 2.~2", IDM_ITEM2_2
                MENUITEM "Item 2.~3", IDM_ITEM2_3
        END

        MENUITEM "~About", IDM_ABOUT
END

This is the sample for the menu we will add to the program of last month. (You can find an exact match of this script in the .RC file in this month's example). Let's try to analyze it.

If you take a look at the script, you should be able to get an impression of how the menu will look. This script describes a main menu with three entries named "Menu 1", "Menu 2" and "About". If "Menu 1" or "Menu 2" is activated it will display a submenu, the third menu, "About", is designed to trigger an event directly.

Some Definitions:

MENU
The Menu identifier is the starting point. After this keyword a whole cluster of definitions is added. Together they build the menu. This cluster must be embedded between the BEGIN and END keywords.
SUBMENU
The submenu keyword is used to declare a submenu. Everything between the BEGIN and END after this keyword defines this menu. In the submenu new submenus may be added.
MENUITEM
Defines a menu item. Just as with SUBMENU, after this keyword the name of the menu is inserted. This is the name as it will appear in the menu. Note that in some places the ~ (tilde) is used. This may be done once in a menu-name. This will result in a underline effect for the letter that is preceded with the tilde. This letter can then be used as a fast access to the menu item.
SEPARATOR
This keyword is used in "Menu 2". If a MENUITEM SEPARATOR is used, it will create a line in the menu. In our example the Item 2.1 and Item 2.2 will be separated by a line.

With the KEYWORDS described until now we can build fairly complete menus. There are however, a lot more possibilities, like buttons, etc. These will be addressed in a future column. There is another thing that I haven't explained about the above example: after almost every line there is a word (identifier) like IDM_MENU1_1. These are called resource identifiers.

Resource identifiers

The .RC file describes the resources, but the compiler (as well as the OS) must have a way to identify every resource used by a program. This is why every resource has a resource identifier. A resource identifier is a constant that is usually defined in a .h (header) file and is included in all the program parts that need to know the resource ID. The header file for Sample 3 looks like this:

#define MAIN           1

#define IDM_MENU1      100
#define IDM_ITEM1_1    101
#define IDM_ITEM1_2    102

#define IDM_MENU2      200
#define IDM_ITEM2_1    201
#define IDM_ITEM2_2    203
#define IDM_ITEM2_3    204

#define IDM_ABOUT      300

As you can see, all the menu-item identifiers have a different constant assigned to them. You're relatively free to choose these numbers. Menu items in the same menu must have different numbers. Or, if you want two menu items to trigger the same event, menu items should have the same ID. The first file in which we include this header file is the resource file (.RC). This way the resource compiler knows which ID's to attach to the separate items. We also want to use this include file in the file in which we handle the events triggered by a menu action. To accomplish this, the following line is added to simple3.rc and simple3.cpp.

#include "simple3.h"

Why does the .cpp file need to know these ID's? Well, every time a menu item is activated, a WM_COMMAND message is sent to the client window. This means we can let the window procedure we defined for our little program intercept this message. All we have to do in the code that handles this message is determine which menu-item is pressed. (The resource identifier is used for this.) To understand how this works, let's take a look at the WM_COMMAND message.

param1   USHORT  uscmd         // Command value.  
 
param2   USHORT  ussource      // Source type.  
         USHORT  uspointer     // Pointer-device indicator.  
 
returns  ULONG   ulReserved    // Reserved value, should be 0.

The only thing we need to know now is how to detect the right menu-ID. For this we only need the message parameter 1. Message parameter 2 can be used to pass information about the source that triggered the menu event. For now, we're not interested in this. (Once again, this will be addressed in a future article, so keep reading.)

You've noticed that the first message parameter is a USHORT. This means we can't simply treat it as a ULONG (which every message parameter is). We need to extract the SHORT from this ULONG. For this, there is a nice macro called SHORT1FROMMP. Take a look at the following piece of code:

case WM_COMMAND:
     {
     switch (SHORT1FROMMP(mpParm1))
          {
          case IDM_ITEM1_1:
               {
               // Handling menu item 1.1
               }
          break;

          case IDM_ITEM1_2:
               {
               // Handling menu item 1.2 
               }
          break;
          }
     }
break;

What you do in the separate menu item routines is dependant on your application. In our example, we will use the WinMessageBox API to make something happen when a menu item is clicked. (This API displays a popup box in which text can be displayed. See the sample code for this.)

Using the menu resource

At the moment we've got a resource file and we know what kind of messages the menu generates and how to handle them. If we leave it at this, we will have a window without a menu. What we need is a way to attach the defined window to the frame window. This is where the FCF flags come in (Frame Creation Flags). The Frame creation flags have been loosely mentioned in an earlier column and we will go deeper into them next month. All we've got to know for now is the FCF_flag we need to attach the menu. This can be done by adding the FCF_MENU flag.

After this is done, we need to tell the frame window upon creation which resource to load. We gave the menu the resource MAIN (1). In this code sample this is added (in previous samples this was set to 0).

WinCreateStdWindow (HWND_DESKTOP,     // Parent
                    WS_VISIBLE,       // Style (visible)
                    &flFrameFlags,    // Creation flags
                    "SampleClass",    // Class name
                    "Simple 2 for OS/2 e-Zine!",// Titlebar text
                    0,                // Client style
                    NULLHANDLE,       // Resource handle
                    MAIN,             // Frame ID  <======= This is it
                    &hwndClient);     // Client handle

In future articles we'll see that all the controls that a frame window has to load that are controlled by the FCF-flags must have the same ID and the Frame ID.

WM_COMMAND and the button.

The WM_COMMAND message is sent by the menu items as they are clicked. But this command can also be invoked by other controls, most noticeably the button control. The button control will also send a WM_COMMAND to its parent when it is clicked. So, if we know the ID of the button, we should be able to intercept a button click in the handling of WM_COMMAND. (Make sure that the ID doesn't double one of the menu ID's).

Compiling .RC files

RC files must be compiled just as C++ files. To do this, we can use the RC compiler that comes with OS/2. With this compiler a .RC script file can be compiled to a .RES file which can be linked to an EXE file (also by RC). Most compiler packages though can handle resource files from within the IDE. This means we don't have to bother about doing command-line stuff. (In the Borland C++ compiler you simply include the .RC file in the project and everything will be handled.)

Concluding Notes

There is a lot that can be done with resources. What we've gone through now is only scratching the surface of what's possible. This means we will see a lot more of resources in future columns. For some applications it's an ideal way to create a program with very little programming effort. (The Smalled HTML Extensions are an example of this; everything you see in that application is done with resources, there isn't a control that is hard coded in there.)

All changes that are new in the SIMPLE3.CPP (ZIP, 16.7k) program are marked with NEW to make it easier to figure out.