/****************************************************************************
*                                                                           *
*  File Name   : RECODISP.C                                                 *
*                                                                           *
*  Description : Example PENPM Aware Application                            *
*                                                                           *
*  Copyright (C) 1992 IBM Corporation                                       *
*                                                                           *
*  NOTE: The RECODISP program can NOT run by itself. It must be given as    *
*        a command name in Gesture setting page. Click right mouse button   *
*        on a program object in work place shell, for example, calculator.  *
*        Click the arrow in Open and then Settings. Get the Gesture page    *
*        and edit a gesture. Highlight a gesture and click the Edit button. *
*        Give RECODISP as Command and click OK button. If you give the      *
*        gesture on the Calculator, the Pen for OS/2 will initiate the      *
*        RECODISP program.                                                  *
*                                                                           *
*        The RECODISP.EXE must be in a directory given in "PATH=" in        *
*        CONFIG.SYS file.                                                   *
*                                                                           *
*        If you access (read/write) any file, you have to give full path    *
*        for the file.                                                      *
*                                                                           *
*        If you call DosQueryCurrentDir or DosQueryCurrentDisk, the root    *
*        directory of boot disk is usually returned, but this is NOT        *
*        always true. It is unpredictable.                                  *
*                                                                           *
*        While you process the WM_RECO_COMMAND message, do NOT give a       *
*        message/dialog box. Simply post a message to yourself to give      *
*        such box. Otherwise deadlock can occur.                            *
*                                                                           *
*        The RECODATA can be retrieved by RedRecoDataFromEnv at the         *
*        WM_CREATE message and by WM_RECO_COMMAND message. The command,     *
*        arguments, and prefix command handling is NOT same in two messages.*
*        Please check the codes below.                                      *
*                                                                           *
*  DISCLAIMER OF WARRANTIES.  The following [enclosed] code is              *
*      sample code created by IBM Corporation. This sample code is not      *
*      part of any standard or IBM product and is provided to you solely    *
*      for  the purpose of assisting you in the development of your         *
*      applications.  The code is provided "AS IS", without                 *
*      warranty of any kind.  IBM shall not be liable for any damages       *
*      arising out of your use of the sample code, even if they have been   *
*      advised of the possibility of such damages.                          *
*                                                                           *
****************************************************************************/

#define  INCL_DOS
#define  INCL_PM
#define  INCL_GPI
#include <os2.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include <penpm.h>
#include "recodisp.h"

int main(VOID)
{
QMSG     qmsgMain;
HMQ      hmqMain;
HWND     hwndFrame;
HWND     hwndClient;
ULONG    flCreate;

  habMain = WinInitialize( (ULONG)NULL );
  hmqMain = WinCreateMsgQueue( habMain, 0L );

  WinRegisterClass( habMain,
                    "MyClient",
                    (PFNWP)ClientWndProc,
                    CS_SIZEREDRAW,
                    0UL );

  /************************************************************************
  * Tell PM that we want the standard frame window, but we will set the   *
  * the size and position the frame window.  This frame window will       *
  * surround a "MyClient" class client window.                            *
  ************************************************************************/
  flCreate = FCF_STANDARD & ~FCF_SHELLPOSITION | FCF_ICON;

  hwndFrame = WinCreateStdWindow( HWND_DESKTOP,
                                  0L,
                                  (PULONG) &flCreate,
                                  "MyClient",
                                  "",
                                  0L,
                                  (HMODULE)NULL,
                                  ID_MAIN,
                                  (PHWND)&hwndClient );

  /*************************************************************************
  * Tell PM to display our window at the designated size and place.        *
  *************************************************************************/
  WinSetWindowPos( hwndFrame,
                   HWND_TOP,
                   50, 50, 540, 100,
                   SWP_SIZE | SWP_MOVE | SWP_ACTIVATE | SWP_SHOW );

  while( WinGetMsg( habMain, &qmsgMain, (HWND)NULL, 0, 0 ) )
  {
    WinDispatchMsg( habMain, &qmsgMain );
  }

  WinDestroyWindow( hwndFrame );
  WinDestroyMsgQueue( hmqMain );
  WinTerminate( habMain );
  return(FALSE);
}

MRESULT EXPENTRY ClientWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
HPS             hpsClient;
RECTL           rcClient;
APIRET          RedRC;
HWND            hwndHotspot;
HWND            hwndActive;
HWND            hwndFocus;
ULONG           nLen;
HRECO           hReco;
RECOID          rID;
CHAR            achRecoSubsys[15], achRecoEvent[15];
ULONG           ulEventCnt;
ULONG           ulVID;              /* virtual ID of event                */
ULONG           ulCC;               /* charactor code of event            */
static POINTL   ptText, ptText2;
static RECODATA *pRecodata;
static BOOL     fClearClientWindow = FALSE;
static CHAR     achLine[STR_LEN];
static CHAR     achLine2[STR_LEN];
static CHAR     achMsg[STR_LEN], achMsg2[STR_LEN], achMsg3[STR_LEN];
static PCHAR    pCmd, pArg, pPrefixCmd;
static BOOL     fInit = FALSE;

  switch( msg )
  {
     case WM_CREATE:
     {
        /********************************************************************
        * The APIs being used in this procedure depend on the PenPM sub-    *
        * system being correctly initialized.  Thus we check to make sure   *
        * PenPM is up before we do anything else.                           *
        ********************************************************************/
        if( WrtWaitActive( WRT_IMMEDIATE_RETURN ) )
        {
           /******************************************************************
           * If PenPM is NOT active then put up a message box and terminate  *
           * the program.                                                    *
           ******************************************************************/
           RedRC = 0;           /* fool the compiler to aviod warning error */
           GetString(hwnd, STR_1, FALSE, RedRC, TRUE);
           break;
        }

        /********************************************************************
        * Register reco cmd with PenPM.                                     *
        * If you have another command handler running and if you want to    *
        * remove it, the RedQueryRecoCommand and RedDeregisterRecoCommand   *
        * API's can be used.                                                *
        ********************************************************************/
        RedRC = RedRegisterRecoCommand( CMD_STR, ID_CMD, hwnd );
        if (RedRC)
        {
           GetString(hwnd, STR_5, TRUE, RedRC, TRUE);
           break;
        }

        /********************************************************************
        * Retrieve RECODATA from environment.                               *
        * First put NULL for the buffer pointer to get the actual size.     *
        * If NULL is given as buffer pointer, then NO error returned.       *
        * If it is not NULL but the lenght is not sufficient, then          *
        * REDERR_BUF_TOO_SMALL is returned.                                 *
        ********************************************************************/
        nLen = 0;
        RedRecoDataFromEnv( NULL, &nLen );

        /********************************************************************
        *  Check whether this program is initiated by Pen system.           *
        ********************************************************************/
        if (!nLen)
        {
           GetString(hwnd, STR_6, FALSE, RedRC, FALSE);
           fInit = TRUE;
           nLen = sizeof(RECODATA) + CCHMAXPATH;
           WinLoadString(habMain,
                         0UL,
                         STR_7,
                         sizeof(achLine2),
                         achLine2);
        }

        /*****************************************************************
        *  The actual length of data is returned in nLen.                *
        *****************************************************************/
        pRecodata = (RECODATA *) malloc(nLen);
        if (!pRecodata)
        {
           GetString(hwnd, STR_4, FALSE, RedRC, TRUE);
           break;
        }

        if (!fInit)
        {
           RedRC = RedRecoDataFromEnv( pRecodata, &nLen );
           if (RedRC)
           {
              GetString(hwnd, STR_2, TRUE, RedRC, TRUE);
              break;
           }
        }

        /****************************************************************
        *  Allocate memory for command, argument, and prefix command.   *
        ****************************************************************/
        pCmd = malloc(LEN_CMD);
        pArg = malloc(LEN_ARG);
        pPrefixCmd = malloc(LEN_PCMD);
        if (!pCmd || !pArg || !pPrefixCmd)
        {
           GetString(hwnd, STR_4, FALSE, RedRC, TRUE);
           break;
        }

        /*****************************************************************
        *  Set position in window.                                       *
        *****************************************************************/
        ptText.x = ptText2.x = 20;
        ptText.y = 26;
        ptText2.y = 10;

        /*****************************************************************
        *  Load the strings used to give the RECODATA.                   *
        *****************************************************************/
        WinLoadString(habMain, 0UL, STR_3, sizeof(achMsg), achMsg);
        WinLoadString(habMain, 0UL, STR_8, sizeof(achMsg2), achMsg2);
        WinLoadString(habMain, 0UL, STR_9, sizeof(achMsg3), achMsg3);

        /*****************************************************************
        *  When the RECODATA is retrieved from environment, the Command, *
        *  Argument, and PrefixCommand are attached after the RECODATA.  *
        *  So, the pszCmd points after the pszPrefixCmd, the pszArg      *
        *  points after the command data, and so forth.                  *
        *  All three data are terminated by NULL character.              *
        *  But the retrieval of RECODATA by WM_RECO_COMMAND message is   *
        *  quite different. Check the message below.                     *
        *****************************************************************/
        WinPostMsg(hwnd, WM_RECO_INFO, NULL, NULL);
        break;
     }

     case WM_RECO_COMMAND:
     {
        /********************************************************************
        *  Replace this message handling code with yours for your own       *
        *  command handler based upon the command and argument in RECODATA. *
        ********************************************************************/

        fInit = FALSE;

        /********************************************************************
        * Copy the RECODATA.                                                *
        ********************************************************************/
        *pRecodata = * (RECODATA *) PVOIDFROMMP(mp2);

        /*****************************************************************
        *  The RECODATA is retrieved, but the command, argument, and     *
        *  prefix command must be copied too.  These data are valid      *
        *  while the WM_RECO_COMMAND message is processed.               *
        *****************************************************************/
        if (pRecodata->pszCommand)
        {
           strcpy(pCmd, pRecodata->pszCommand);
        }
        if (pRecodata->pszCmdArgs)
        {
           strcpy(pArg, pRecodata->pszCmdArgs);
        }
        if (pRecodata->pszPrefixCmd)
        {
           strcpy(pPrefixCmd, pRecodata->pszPrefixCmd);
        }

        /*****************************************************************
        * Do NOT call WinMessageBox or WinDlgBox because deadlock can    *
        * occur. You can check the command or recoID field in RECODATA,  *
        * and then, based upon the command or ID, you can run program    *
        * designated for the command or ID.                              *
        *****************************************************************/

        WinPostMsg(hwnd, WM_RECO_INFO, NULL, NULL);
        return( (MRESULT)(TRUE) );
     }

     case WM_RECO_INFO:
        /*****************************************************************
        * Pull out desired data                                          *
        *****************************************************************/
        if (!fInit)
        {
           hwndHotspot = pRecodata->hwnd;
           hwndActive  = pRecodata->hwndActive;
           hwndFocus   = pRecodata->hwndFocus;

           WinLoadString(habMain, 0UL, STR_3, sizeof(achMsg), achMsg);
           sprintf(achLine, achMsg, hwndHotspot, hwndActive, hwndFocus);

        }
        /******************************************************************
        * Invalidate the client window to force a repaint                 *
        ******************************************************************/
        WinInvalidateRect( hwnd, NULL, TRUE );

        break;

     case WM_PAINT:
     {
        hpsClient = WinBeginPaint( hwnd, (HPS) NULL, &rcClient );
        WinFillRect( hpsClient, &rcClient, SYSCLR_WINDOW );

        /********************************************************************
        * If there was a clear cmd, then clear the client window.           *
        ********************************************************************/
        if( fClearClientWindow )
        {
           fClearClientWindow = FALSE;
        }
        else
        {
           GpiCharStringAt(hpsClient,
                           &ptText,
                           STR_LEN,
                           fInit ? achLine2 : achLine);
           if (!fInit)
           {
              /************************************************************
              *  Give the subsystem and event names if RECODISP is        *
              *  initiated by gesture.                                    *
              ************************************************************/

              /************************************************************
              *   Get the reco ID and reco handle from RECODATA.          *
              *   These will give event and subsystem names.              *
              *   Get the virtual ID and character code from RECODATA.    *
              ************************************************************/
              rID = pRecodata->id;
              hReco = pRecodata->hReco;
              ulVID = pRecodata->virtual_id;
              ulCC = pRecodata->char_code;

              /****************************************************************
              *  Get the Reco Subsystem name and Number of Events from handle.*
              *  If you have the subsystem name, then the RedQueryRecoHandle  *
              *  API can be used to get the reco handle. achRecoSubsys has    *
              *  "GESTURE".                                                   *
              ****************************************************************/
              RedQueryRecoSubsystem(hReco, achRecoSubsys, &ulEventCnt);

              /****************************************************************
              *  Get the Event Name.                                          *
              *  If the event name is available, then the event ID can be     *
              *  retrieved by RedRecoIDFromName.                              *
              ****************************************************************/
              RedRecoNameFromID(hReco, rID, achRecoEvent);

              if (ulVID <= 32)
              {
                 ulVID--;            /* Adjust subscript */
                 sprintf(achLine2, achMsg2, achRecoEvent, achVEID[ulVID]);
              }
              else
              {
                 /***********************************************************
                 *  For alphabet chacter gesture                            *
                 ***********************************************************/
                 sprintf(achLine2, achMsg3, achRecoEvent, ulCC );
              }

              GpiCharStringAt(hpsClient,
                              &ptText2,
                              STR_LEN,
                              achLine2);
           }
        }
        WinEndPaint( hpsClient );
        break;
     }

     case WM_COMMAND:
     {
        switch( SHORT1FROMMP(mp1) )
        {
           case ID_CLEAR:
           {
              /***************************************************************
              * If there was a clear cmd, then tell the procedure to clear   *
              * the client window.                                           *
              ***************************************************************/
              fClearClientWindow = TRUE;
              WinInvalidateRect( hwnd, NULL, TRUE );
              break;
           }

           case ID_EXIT:
           default:
              WinPostMsg(hwnd, WM_CLOSE, NULL, NULL);

              /**************************************************************
              * Free memory allocated for Recodata, command, argument,      *
              * and prefix command.                                         *
              **************************************************************/
              free(pRecodata);
              free(pCmd);
              free(pArg);
              free(pPrefixCmd);
              break;
        }
        break;
     }

     default:
     {
       return( WinDefWindowProc( hwnd, msg, mp1, mp2 ) );
     }
  }
  return FALSE;
}

/******************************************************************************
*                                                                             *
* Function Name: GetString                                                    *
*                                                                             *
* Description : Load string for each string ID.                               *
*                                                                             *
* Input: hwnd    : Client window handle                                       *
*        nStr_ID : String ID                                                  *
*        fPr     : print flag                                                 *
*        RedRC   : if fPr is TRUE, print this return code.                    *
*        fRet    : if FALSE, return without deregistering the command handler.*
*                                                                             *
* Output:  None.                                                              *
*                                                                             *
******************************************************************************/
VOID  GetString(HWND hwnd, ULONG nStr_ID, BOOL fPr, APIRET RedRC, BOOL fRet)
{
CHAR   achLine[80], achMsg[80];

    WinLoadString(habMain,
                  0UL,
                  nStr_ID,
                  sizeof(achMsg),
                  achMsg);
    if (fPr)
    {
       sprintf(achLine, achMsg, RedRC);
    }
    WinMessageBox( HWND_DESKTOP,
                   hwnd,
                   fPr ? achLine : achMsg,
                   fRet ? NULL : (PSZ) "Warning",
                   1, MB_OK );

    if (!fRet)
    {
       return;
    }

    /*****************************************************************
    *  Deregister the reco command.                                  *
    *****************************************************************/
    RedDeregisterRecoCommand(hwnd);

    WinSendMsg( hwnd, WM_CLOSE, NULL, NULL );
    return;
}
