Making Help Manager Useable

From EDM2
Jump to: navigation, search

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 frustating, 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 */
#define PAGE_COUNT (sizeof( nbpage ) / sizeof( NBPAGE ))
#define PAGE_FOO  0
#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!