/****************************************************************************
**
**  Name:  f1hook.c
**
**  Description:
**     This is the source code to support F1 key support for context
**     sensitive help, from any dialog box or window.
**
**  $Log:   S:/tbird/arcppc/cliulib/f1hook.c_v  $
** 
**    Rev 1.0   17 Jan 1997 09:11:36   kevin
** Initial revision.
** 
**    Rev 1.0   03 Jun 1996 11:15:18   gene
** Initial revision.
** 
**    Rev 1.0   07 Sep 1995 10:08:22   gene
** Initial revision.
** 
**    Rev 1.7   16 Sep 1993 13:48:16   ron
** put in an array bounds check for f1 registration (firewall).
** 
**    Rev 1.6   10 Sep 1993 13:59:20   ron
** changed HANDLE to LONG for parameters passed by Actor
** 
**    Rev 1.5   04 Aug 1993 16:45:38   ron
** ppr8738: needed to remove window handles from array if another index
** registers the same one (meaning that Windows is reusing a handle).
** 
**    Rev 1.4   09 Jul 1993 16:42:00   ron
** also keep track of curHelpIndex upon actual f1 press
** 
**    Rev 1.3   08 Jul 1993 15:28:04   ron
** needed an additional check on window handles to avoid grabbing some other
** application's F1 press.
** 
**    Rev 1.2   08 Jul 1993 15:02:52   ron
** removed unnecessary check for extended key bit.
** 
**    Rev 1.1   08 Jul 1993 11:04:26   ron
** added f1hook for F1 use in dialogs
** 
**    Rev 1.0   06 Jul 1993 16:11:58   ron
** Initial revision.
** 
**  $Header:   S:/tbird/arcppc/cliulib/f1hook.c_v   1.0   17 Jan 1997 09:11:36   kevin  $
**
**  Copyright (C) 1992 Microtek International.  All rights reserved.
**
*****************************************************************************/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/
#ifndef _BASEWIND_
#include "basewind.h"
#endif

#ifndef _VERSIONS_
#include "versions.h"
#endif

#ifndef _HLPENTRY_
#include "hlpentry.h"
#endif

#include "windows.h"
#include <string.h>

                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/

/* See the Help Indexes in inc\hlpentry.h */
#define MAXENTRIES 40
typedef struct {
   WORD   helpEntry;
   HANDLE hWnd;
} HELP_REF;
#define EXTENDEDKEY 0x1000000L
#define NOHELP -1

                       /****************************
                        *                          *
                        *    EXTERNAL VARIABLES    *
                        *                          *
                        ****************************/

HELP_REF  HelpRefArray[MAXENTRIES];
FARPROC  lpProcInstance = NULL;
FARPROC  OldHookProc = NULL;
S16      curHelpIndex = 0;

                       /****************************
                        *                          *
                        *     LOCAL PROTOTYPES     *
                        *                          *
                        ****************************/

                       /****************************
                        *                          *
                        *      EXECUTABLE CODE     *
                        *                          *
                        ****************************/

/*--------------------------------------------------------------------------
**  ActivateHelpEntry
**
**  Purpose:
**     Find out if the handle passed as the argument is in the HelpRefArray,
**     and set curHelpIndex to the appropriate index if it is.  If it isn't,
**     see if the window has a parent (recursively) which *is* in the array.
**     If it has no parent, or no parent in the HelpRefArray, set 
**     curHelpIndex to a negative value so that we won't grab F1 ourselves
**     if it is pressed when a different application is active.
**
**  Input:
**     hWnd:  Handle of window getting focus.
**
**  Output:
**     None.
----------------------------------------------------------------------------*/
void ActivateHelpEntry(HANDLE hWnd)
{
   int indHelp;
   while (hWnd != NULL) {
      for (indHelp = 0; indHelp < MAXENTRIES; indHelp++) {
         if (HelpRefArray[indHelp].hWnd == hWnd) {
            curHelpIndex = indHelp;
            return;
         }
      }
      hWnd = GetParent(hWnd);
   }
   curHelpIndex = NOHELP;
   return;
}

/*--------------------------------------------------------------------------
**  IsMine
**
**  Purpose:
**     Find out if the handle passed as the argument is in the HelpRefArray,
**     or if a parent window is (if not, ignore this message).
**
**  Input:
**     hWnd:  Handle of window getting focus.
**
**  Output:
**     None.
----------------------------------------------------------------------------*/
BOOL IsMine(HANDLE hWnd)
{
   int indHelp;
   while (hWnd != NULL) {
      for (indHelp = 0; indHelp < MAXENTRIES; indHelp++) {
         if (HelpRefArray[indHelp].hWnd == hWnd) {
            curHelpIndex = indHelp;
            return (TRUE);
         }
      }
      hWnd = GetParent(hWnd);
   }
   return (FALSE);
}

/*--------------------------------------------------------------------------
**  F1HookProc
**
**  Purpose:
**     Look at all the WM_SETFOCUS and WM_ACTIVATE messages, and if any
**     are going to PowerViews windows that we know about, set the current
**     help entry to the help entry for that window or dialog.  Look at
**     all WM_KEYDOWN messages if we have a current help entry point, and
**     bring up Windows help if the F1 key is pressed.
**
**  Input:
**     nCode:  If < 0, pass this on to DefHookProc, otherwise handle it here
**     wParam:  The word message param. (NULL)
**     lParam:  The long message param. (A pointer to the message struct)
**
**  Output:
**     None.
----------------------------------------------------------------------------*/
DWORD EXPORT F1HookProc(int nCode, WORD wParam, DWORD lParam)
{
   LPMSG fpMsg;
   HANDLE thisHWND;
   WORD thisMsg;
   if (nCode < 0) {
      /* Note: it's not clear if we're supposed to call this if OldHookProc
       *       is NULL.  It appears we are supposed to, but I'm not sure.
       */
      return (DefHookProc(nCode, wParam, lParam, (FARPROC FAR *) &OldHookProc));
   }
   /*
    * Page 540 of Windows API Volume 1 says that lParam points to the
    * message structs, but doesn't say what type of pointer.  I'm guessing
    * FAR...
    */
   fpMsg = (LPMSG) lParam;
   if (fpMsg == NULL) {
      return (0L);
   }
   thisMsg = fpMsg->message;
   thisHWND = fpMsg->hwnd;
   switch(thisMsg) {
      case WM_SETFOCUS:
         thisHWND = GetActiveWindow(); /* focus might be button... */
         ActivateHelpEntry(thisHWND); /* set to this or a parent window */
         break;
      case WM_ACTIVATE:
         if (fpMsg->wParam == 0) { /* window losing focus */
            if (curHelpIndex >= 0) {
               if (HelpRefArray[curHelpIndex].hWnd == thisHWND) {
                  curHelpIndex = NOHELP;
               }
            }
         }
         else { /* window gaining focus */
            thisHWND = GetActiveWindow(); /* focus might be button... */
            ActivateHelpEntry(thisHWND); /* set to this or a parent window */
         }
         break;
      case WM_KEYDOWN:
         /* Careful -- efficiency probably matters a lot here */
         if (curHelpIndex >= 0) {
            /* Was it F1? */
            if (fpMsg->wParam == VK_F1) {
               thisHWND = GetActiveWindow(); /* focus might be button... */
               if (IsMine(thisHWND)) {
                  WinHelp(thisHWND, "PWRVIEWS.HLP",
                     HELP_CONTEXT, HelpRefArray[curHelpIndex].helpEntry);
                  return (1L);
               }
            }
         }
         break;
   }
   return (DefHookProc(nCode, wParam, lParam, (FARPROC FAR *) &OldHookProc));

} /* end of F1HookProc() */

/*--------------------------------------------------------------------------
**  RegisterHelpEntry
**
**  Purpose:
**    Record the Window Handle of a PowerViews window or dialog along with
**    the help entry to use for it.
**
**  Input:
**     hInd:       The index in the handle/entry array for this window.
**     hWnd:       The parent window handle.
**     helpEntry:  The help entry to use if F1 is pressed over that window.
**
**  Output:
**     None.
----------------------------------------------------------------------------*/
RETCODE EXPORT RegisterHelpEntry(U16 hInd, LONG hWndTmp, WORD helpEntry)
{
   HANDLE hWnd;
   int indHelp;
   hWnd = (HANDLE) hWndTmp; /* avoid Actor signed int problems */
   if (hInd >= MAXENTRIES) return (GOOD); /* make up an error code here */
   /* remove any window that USED to have this handle */
   for (indHelp = 0; indHelp < MAXENTRIES; indHelp++) {
      if (HelpRefArray[indHelp].hWnd == hWnd) {
          HelpRefArray[indHelp].hWnd = 0;
      }
   }
   HelpRefArray[hInd].hWnd = hWnd;
   HelpRefArray[hInd].helpEntry = helpEntry;
   return(GOOD);
}

/*--------------------------------------------------------------------------
**  HookF1Filter
**
**  Purpose:
**    Hook up the F1 Filter on PowerViews startup.
**
**  Input:
**     None.
**
**  Output:
**     None.
----------------------------------------------------------------------------*/
RETCODE EXPORT HookF1Filter()
{
   HANDLE hModule;
   hModule = GetModuleHandle("CLIULIB");
   lpProcInstance = (FARPROC) GetProcAddress(hModule, (LPSTR) "#18");
   OldHookProc = SetWindowsHook(WH_GETMESSAGE, lpProcInstance);
   return(GOOD);
}

/*--------------------------------------------------------------------------
**  UnhookF1Filter
**
**  Purpose:
**    Unhook the F1 Filter on PowerViews exit.
**
**  Input:
**     None.
**
**  Output:
**     None.
----------------------------------------------------------------------------*/
RETCODE EXPORT UnhookF1Filter()
{
   UnhookWindowsHook(WH_GETMESSAGE, lpProcInstance);
   return(GOOD);
}
