Making Help Manager Useable

By Samuel Audet

Last Modified: May 7, 2001 20:07:02 EDT.

First of all, some explanations. When trying to make the usual "help tables" work with notebook dialogs, it seems they do not accept the notebook dialogs as HELPITEMs. This is very frustrating, as it SHOULD work that way. Oh well, that's how it is... Now, here's how I think is the best (ie.: not perfect) way to implement IPF in Notebook dialogs.

First, load and associate the usual help tables with the main dialog, but leave out the notebook from it. For example, you have an "OK" and "Save" Button, here's what you'd put in the resource file: HELPTABLE MAINTABLE BEGIN HELPITEM ID_MAIN, SUBTABLE_MAIN, 1000 END

HELPSUBTABLE SUBTABLE_MAIN BEGIN HELPSUBITEM ID_OKPB, 1100 HELPSUBITEM ID_SAVEPB, 1100 END Note: as the "extended help" for the MAIN HELPITEM, include a more general help than only the buttons, because if you leave cracks in the following method, the user expects to see something that gives information on the whole notebook.

Now in the C code, you will need a big structure containing all information needed on the notebook dialog. I always use such a structure when using Notebooks. It permits me, for example to delay loading of dialog windows to when the user requests it, or to easily find a dialog window handle to send a message to it (like we will need here), etc. Thanks Rick Fishman for that great idea everyone should use in Notebooks! Here's what I use: /* notebook page info structure */

typedef struct _NBPAGE {   PFNWP    pfnwpDlg;    /* Window procedure address for the dialog */ PSZ     szStatusLineText;  /* Text to go on status line */ PSZ     szTabText;   /* Text to go on major tab */ ULONG   idDlg;       /* ID of the dialog box for this page */ BOOL    skip;        /* skip this page (for major pages                             with minor pages) */ USHORT  usTabType;   /* BKA_MAJOR or BKA_MINOR */ ULONG   ulPageId;    /* notebook page ID */ HWND    hwnd;        /* set only when page frame is loaded */

} NBPAGE, *PNBPAGE; Then, initialize a global variable of this structure with the values, for example: NBPAGE nbpage[] = {  {wpFoo,  "Tab 1 of 4", "~Foo",  ID_FOO,  FALSE, BKA_MAJOR, 0, 0}, {wpFoo2, "Tab 2 of 4", "Foo~2", ID_FOO2, FALSE, BKA_MAJOR, 0, 0}, };

/* some constants so that we don't have to modify code if we add pages */
 * 1) define PAGE_COUNT (sizeof( nbpage ) / sizeof( NBPAGE ))
 * 2) define PAGE_FOO 0
 * 3) define PAGE_FOO2 1

int main {  ... } When cooking up your notebook, make AT LEAST the window handle available.

In the main dialog (or client) window procedure, include the following. You will need to find your Notebook window handle (usually with a window ID). case WM_HELP: {  HWND hwndfocus = WinQueryFocus(HWND_DESKTOP); HWND hwndNB = WinWindowFromID(hwnd,ID_NB); int i;

/* this is useful when the user presses Help button and the focus is on the notebook tabs themselves */

if(hwndfocus == hwndNB) {     WinSendMsg(         LONGFROMMR(WinSendMsg(hwndNB, BKM_QUERYPAGEWINDOWHWND,            WinSendMsg(hwndNB,BKM_QUERYPAGEID,0,MPFROMSHORT(BKA_TOP))                                                 , NULL))                                                     ,WM_HELP,0,0); return 0; /* prevents the notebook unfriendly help manager to interfere */ }

/* this is useful when the user presses Help button and the focus is on one of the notebook dialog controls */

for(i = 0; i < PAGE_COUNT; i++) if(WinIsChild(hwndfocus,nbpage[i].hwnd)) {        WinSendMsg(nbpage[i].hwnd,WM_HELP,0,0); return 0; /* prevents the notebook unfriendly help manager to interfere */ }

break; /* if focus is not on notebook controls or the notebook, let the usual help mechanism handle it */

case WM_CONTROL: {  switch(SHORT2FROMMP(mp1)) {

/* this is useful when the user presses F1 on a notebook tab */ case BKN_HELP:

WinSendMsg(LONGFROMMR(WinSendMsg(WinWindowFromID(hwnd,ID_NB),                BKM_QUERYPAGEWINDOWHWND, mp2 /* this is ulPageId */, NULL))                                               ,WM_HELP,0,0); return 0; /* bug off Help Manager */ } } Note: if you try to send a "WM_HELP" to the notebook when "hwndfocus == hwndNB" thinking you are smart because BKN_HELP is sent, and an error occurs, the notebook sends back a WM_HELP to its owner and keeps getting back more and more WM_HELP and BKN_HELP around and around, until you're quick enough to remove the focus from the notebook... don't ask me why. It's a miracle it even works.

Then, as you might have imagined where all those WM_HELP are sent to, include the following into all your Notebook dialog window procedures. /* this is useful when the user presses F1 in  any of the dialog controls owned by this window. Also, all the previous kludges send WM_HELP here. */

case WM_HELP: WinSendMsg(WinQueryHelpInstance(hwnd),             HM_DISPLAY_HELP,MPFROMP("FooPanel"),              MPFROMSHORT(HM_PANELNAME)); return 0; /* grrr, beat off Help Manager */ Next, in your .HLP file, just include the newly defined panel name or reference you have included in all the WM_HELP messages like above. I also recommend a global variable indicating successful loading of the help file (into a help instance) so that the user won't have to mess around with errors and possible crashes.

Tada! Great help management in notebooks!