Chapter 29 |
[ Contents ]
|
Beginning with OS/2 1.2, IBM introduced an
addition to the Presentation Manager interface (touted as the "Help
Manager") that allowed an application to add both general help and
field help online. (With 1.3, IBM published the previously undocumented
method for creating online books, which are viewed using the
system-supplied utility VIEW.EXE). It should be noted, however,
that while this capability is very appealing, it is by no means added
to an application quickly; in fact, well-written online help can take
on the average of 1 day/3000 lines of code to complete for the text
alone. (This figure is based on personal experience.) The upside of
this is that, for most Presentation Manager applications,
programmers do not have to think about his designing the
programs; online help can be added at any time, providing that the
source code to the application is available.
There are at
least three parts to the help component of any application: the source
code, the HELPTABLEs, and the definitions of the help panels. The
source code is obviously part of the application source, and includes
the corresponding Win* calls and HM_ messages sent to and received from
the Help Manager. The HELPTABLEs (and HELPSUBTABLEs) are part of
the resource file, and they define the relationships between the
various windows and the corresponding help panels. Finally, the
help panel definitions describe the look as well as the text of the
help panels and are written using a general markup language (GML)-like
language (SCRIPT and Bookmaster users will recognize the help panel
definition language as a subset of the Bookmaster macros they are
familiar with). Let us take a closer look at each of these three
parts in more detail.
The source code is
usually the smallest component of the three, only because it typically
consists of an initialization section and the processing of a few
messages. The initialization section normally goes in the main
routine after the main window is created and follows the next which is
the typical initialization code used in a Presentation Manager
application to create a help instance.
#define HELP_CLIENT 256
HELPINIT hiInit;
CHAR achHelpTitle[256];
HAB habAnchor;
HWND hwndHelp;
HWND hwndFrame;
: // WinInitialize, etc. goes here
// We need to initialize the HELPINIT structure before calling
// WinCreateHelpInstance. See the online technical reference
// for an explanation of the individual fields.
hiInit.cb = sizeof(HELPINIT);
hiInit.ulReturnCode = 0L;
hiInit.pszTutorialName = NULL;
// By specifying 0xFFFF in the high word of phtHelpTable, we are
// indicating that the help table is in the resource tables with
// the id specified in the low word.
hiInit.phtHelpTable = (PHELPTABLE)MAKEULONG(HELP_CLIENT,0xFFFF);
hiInit.hmodHelpTableModule = NULLHANDLE;
hiInit.hmodAccelActionBarModule = NULLHANDLE;
hiInit.idAccelTable = 0;
hiInit.idActionBar = 0;
hiInit.pszHelpWindowTitle = achHelpTitle;
hiInit.fShowPanelId = CMIC_HIDE_PANEL_ID;
hiInit.pszHelpLibraryName = "MYAPPL.HLP";
hwndHelp = WinCreateHelpInstance(habAnchor,&hiInit);
if ((hwndHelp != NULLHANDLE) && (hiInit.ulReturnCode != 0)) {
WinDestroyHelpInstance(hwndHelp);
hwndHelp = NULLHANDLE;
} /* endif */
:
: // Message loop goes here
:
if (hwndHelp != (HWND)NULL) {
WinDestroyHelpInstance(hwndHelp);
hwndHelp = NULLHANDLE;
} /* endif */
As with the relationship between window classes and window
instances, there exists a help manager class of which you create
an instance by calling WinCreateHelpInstance. This function can have one of three outcomes:
If the help instance is successfully created, it becomes the recipient
of any messages that you send and the originator of any messages that
are sent to the active window.
Since a help instance is associated with a "root" window and all of its
descendants, you need to indicate what the root window is.
This is done using the WinAssociateHelpInstance function.
(BOOL)WinAssociateHelpInstance(HWND hwndHelp, // Help instance
HWND hwndWindow); // "Root"
window
Specifing a non-NULL value for hwndHelp indicates that this is
the active window that should be used when determining which help panel
to display. Specifying NULL for this parameter removes the
current association between the help instance and the window
specified. We will see how this is used shortly.
![]() |
Gotcha!
|
The next piece of source code that
you will use in most of your applications deals with the "Help"
pull-down menu and "Help" push-buttons (obviously, if your application
does not contain an action bar or any dialogs, you need not read
this). According, to IBM's guidelines on developing a application
user interface, there should exist on the action bar a pull-down titled
"Help" that contains the following four items:
There can also be an optional fifth item - labeled "Product
information..." - that displays an "About" box when selected.
Fortunately, the Help Manager has four messages that can be sent to it
to process these four menu items. Each of them take no parameters
and are listed in Table 29.1:
Message |
Description |
HM_DISPLAY_HELP | Displays help on using online help. |
HM_EXT_HELP | Displays the "extended" help for the current window. |
HM_KEYS_HELP | Displays the keys help for the current window. |
HM_HELP_INDEX | Displays the help index. |
The
help tables define the relationship between the control windows and the
help panels to be displayed when the user requests help.
Visualizing the help tables as a two-dimensional array of help panel
Ids may make undestanding what they are easier. The first index
into this array is the ID of the window that has been associated with a
help instance via WinAssociateHelpInstance, and the second
index is either a menu item ID or an ID of a child window that
can receive the input focus. To understand how the help
tables are used, we need to understand the sequence of events
beginning with the user pressing F1 and the displaying of the help
panel.
There are obviously many places where errors can occur; the most
frequent one is when the menu item ID/child window ID is not in the
HELPSUBTABLE. When this occurs, the owner window-chain is searched
(steps 3 - 6). If it is still not found, the parent window chain
is also searched. If the ID has not been found after both
searches, the current window is sent a HM_HELPSUBITEM_NOT_FOUND
message, giving it the opportunity to remedy the situation (via a
HM_DISPLAY_HELP message). The default action is to display the
extended help for the current window.
When the ID is found in a HELPSUBTABLE but the panel definition does
not exist, or when any other error occurs (with the exception of
HELPSUBITEM not found described above and when the extended help panel
cannot be determined), the application is sent an HM_ERROR
message. This message contains an error code in the first
parameter that describes the condition causing the error. The typical
response to receiving this is to display a message and then disable the
help manager by calling WinDestroyHelpInstance.
Given this logical view of the help tables, let us look at a sample definition in a resource file.
The tables below describe the
online help panels that correspond to the child windows and menuitems
in the application and its associated dialogs.
HELPTABLE HELP_CLIENT
{
HELPITEM HELP_CLIENT, SUBHELP_CLIENT, EXTHELP_CLIENT
HELPITEM DLG_OPEN, SUBHELP_OPEN, EXTHELP_OPEN
HELPITEM DLG_PRODUCTINFO,
SUBHELP_PRODUCTINFO, EXTHELP_PRODUCTINFO
}
HELPSUBTABLE SUBHELP_CLIENT
{
HELPSUBITEM M_FILE, HELP_M_FILE
HELPSUBITEM MI_NEW, HELP_MI_NEW
HELPSUBITEM MI_OPEN, HELP_MI_OPEN
HELPSUBITEM MI_SAVE, HELP_MI_SAVE
HELPSUBITEM MI_CLOSE,HELP_MI_CLOSE
HELPSUBITEM MI_EXIT, HELP_MI_EXIT
HELPSUBITEM M_HELP, HELP_M_HELP
HELPSUBITEM MI_USINGHELP, HELP_MI_USINGHELP
HELPSUBITEM MI_GENERALHELP, HELP_MI_GENERALHELP
HELPSUBITEM MI_KEYSHELP, HELP_MI_KEYSHELP
HELPSUBITEM MI_HELPINDEX, HELP_MI_HELPINDEX
HELPSUBITEM MI_PRODINFO, HELP_MI_PRODINFO
}
HELPSUBTABLE SUBHELP_SETOPEN
{
HELPSUBITEM DOPEN_EF_FILENAME, HELP_DOPEN_EF_FILENAME
HELPSUBITEM DLG_PB_OK, HELP_DLG_PB_OK
HELPSUBITEM DLG_PB_CANCEL, HELP_DLG_PB_CANCEL
HELPSUBITEM DLG_PB_HELP, HELP_DLG_PB_HELP
}
HELPSUBTABLE SUBHELP_PRODINFO
{
HELPSUBITEM DLG_PB_CANCEL, HELP_DLG_PB_CANCEL
HELPSUBITEM DLG_PB_HELP, HELP_DLG_PB_HELP
}
As is clear from the sample, our application has two dialogs with online
help. Their resource identifiers are DLG_OPEN and DLG_PRODUCTINFO, and
that there are 12 child windows or menu items that belong to the client
window. In each of the HELPSUBITEMS, the window ID is on the left and
the corresponding help panel resource ID is on the right.
![]() |
Gotcha!
|
When your application needs to give the user some information, one of the way it can do so is by using the WinMessageBox
function. This displays a window that contains
application-specified title and text, as well as an optional icon to
the left and one or more predefined push-buttons (e.g., "OK", "Yes",
"Abort", etc.).
(USHORT)WinMessageBox(HWND hwndParent, // parent window
HWND hwndOwner, // owning window
PSZ pszMessage, // pointer to the text
PSZ pszTitle, // pointer to the title
USHORT usHelpId, // help topic id
ULONG ulStyle); // message box style
hwndParent defines the bounding area of the message box; typically, this is HWND_DESKTOP. hwndOwner
specifies the window that "owns" the message box; this window is
disabled while the message box is displayed and is reactivated when the
call returns. pszMessage and pszTitle point to the message box text and title, respectively. usHelpId is used when MB_HELP is specified in ulStyle (see below), and ulStyle
is a combination of MB_* constants. This function returns a
constant that specifies the push-button selected on the message box
(e.g., MBID_OK, MBID_NO, MBID_RETRY, etc.)
As might be imagine, only so much can be said in a small
dialog box. Often, what fits is enough for most users
to figure out what the programmer is trying to say. However, it would
be nice to provide another level of detail for those who would like
more information (i.e., online help). The constant MB_HELP
specifies that a "Help" push-button is requested; this is the only
button that does not cause the function to return. Unfortunately, since
a message box doesn't have to have an application window as the owner
(HWND_DESKTOP will work fine for hwndOwner; this could be used in, for example, a program that simply calls WinMessageBox
with the command line for the message for CMD files), it cannot simply
send the owner a message saying that the help button was pressed. The
system, therefore, provides two ways to display help for message boxes:
using a help hook and using HELPTABLEs. We will look at the latter
method later in the chapter.
A "hook" is a
function that PM calls whenever a certain event occurs. In a
preverted way, we could look at it as subclassing the entire system,
but instead of intercepting messages before the intended recipient
receives them, the application intercepts "events". These events range
from the "code page changed" event to the "DLL has been loaded
with WinLoadLibrary" event and cover 16 different items.
There is, of course, a "help requested" event as well, and it is
this event that we are interested in.
Hooks are installed with WinSetHook and are released with WinReleaseHook Both take the same parameters:
below:
(BOOL)WinSetHook(HAB habAnchor, // HAB of the calling thread
HMQ hmqQueue, // HMQ of the calling thread,
// HMQ_CURRENT for current thread or NULL for
// system-wide hook
USHORT usHookType, // HK_* constant
PFN pfnHookProc, // pointer to the hook procedure
HMODULE hmodProc); // HMODULE containing pfnHookProc
habAncor is the handle to the anchor block of the calling thread. hmqQueue
is the handle of the queue for which events are to be monitored. If
this is HULLHANDLE, events for the entire system are monitored;
however, the hook function - since it will be called by different
processes - must reside in a DLL so that PM can load the function when
needed. usHookType is one of the HK_ constants specifying the event to be monitored. pfnHookProc is a pointer to the event monitoring function (the "hook"). hmodProc
is a handle to the DLL containing the hook function or NULLHANDLE if
hmqQueue is not NULLHANDLE and the hook function resides in the
exutable.
Each of the procedures for the different hook types take different
parameters and return different values. Since we're
interested in the HK_HELP hook, here is the prototype of the hook
function:
(BOOL)pfnHookProc(HAB habAnchor, // HAB of the calling thread
SHORT sMode, // HLPM_* constant
USHORT usTopic, // Topic number
USHORT usSubTopic,// Subtopic number
PRECTL prclPosition);
habAncor is the handle to the anchor block of the thread for which the event occured. sMode indicates the context in which help was requested and is a HLPM_ constant. usTopic and usSubTopic are dependent on the value of sMode.
sMode Is |
usTopic Is | usSubTopic Is |
HLPM_FRAME |
Identifier of the active frame window | Identifier of the window with the focus |
HLPM_MENU | Identifier of the pull-down menu or FID_MENU if the action bar selected
| Identifier of the menu item or submenu item for which help was requested. |
HLPM_WINDOW | Identifier of the message box |
Not used |
![]() |
Gotcha!
|
![]() |
Gotcha!
|
Now that we've seen how easy the
code and resource definitions are, it is time to tackle the most
difficult (to do well) and time-consuming aspect of this
development phase - writing the help panels. While the definition
of the language is large, it is fairly easy to digest. We will
look at only the rudiments of the language; the full language
definition can be gleamed from the online document entitled "IPF
Reference" that is included with the OS/2 Programmer's Toolkit.
The help file (whose file extension is usually ".IPF") is compiled by
the "Information Presentation Facility Compiler" (a.k.a. IPFC) to
produce a ".HLP" file that is read by the Help Manager when WinCreateHelpInstance is called. The source file contains a collection of "tags," which begin with a colon (:), followed by the tag name, an optional set of attributes, and finally a period (.).
Some tags also require a matching "end tag" (e.g., a "begin list"
and "end list" tag), which have no attributes and whose name usually
matches the beginning tag name preceded by an e (e.g., ":sl." and ":esl."). Table 29.3 presents common tags and their meanings.
Tag | Meaning |
:h1. through :h6. | Heading tag. Headings 1 - 3 also have an entry in the table of contents. |
:p. | New paragraph. |
:fn. :efn. |
Footnote and ending tag. |
:hp1. through :hp9. |
Emphasis tag. This requires the matching ending tag (:ehp1. through :ehp9.). |
:link. |
Hypertext link. |
:sl. :esl. |
Simple list and ending tag. |
:ul. :eul. |
Unordered list and ending tag. |
:ol. :eol. |
Ordered list and ending tag. |
:li. |
List item. Used between the list tags to describe the items in the list. |
:dl. :edl. |
Definition list and ending tag. Whereas the
other lists consist of a single element, definition lists consist of a
"data term" and "data definition" (:dt. and :dd., respectively). |
:dt. :dd. |
Data term and data definition tags. |
:dthd. :ddhd. |
Data term heading and data definition heading tags. Also, there are a few special tags that are used only once in a help file. |
:userdoc. :euserdoc. |
Beginning and ending of the document. |
:title. |
The text to be placed in the title bar of the help panels. |
Chapter 29 |
[ Contents ]
|