/*****************************************************************************
*                                                                            *
*  File Name   : ISMPROCS.C                                                  *
*                                                                            *
*  Description : Window Procedures for the ISM_SAMP.EXE                      *
*                                                                            *
*  Function:  This file has all the window procedures for the ISM_SAMP.EXE.  *
*                                                                            *
*  Copyright (C) 1993 IBM Corporation                                        *
*                                                                            *
*      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.                           *
*                                                                            *
*****************************************************************************/

/*****************************************************************************
* Defines                                                                    *
*****************************************************************************/
#define  INCL_PM
#define  INCL_GPIPRIMATIVES

/*****************************************************************************
* Includes                                                                   *
*****************************************************************************/
#include <os2.h>
#include <penpm.h>
#include "ism_samp.h"

BOOL ChangeColor    ( PMYWINDOWDATA pMyWindowData, LONG NewColor );
HRGN CreateClipRect ( PMYWINDOWDATA  pMyWindowData, SHORT NumSegments );
HWND CreateInkControls ( HWND hWnd );
MRESULT APIENTRY InkEditHelp (  HWND hWnd, ULONG ulMsg, MPARAM mp1, MPARAM mp2 );

/*****************************************************************************
* Debug Messages                                                             *
*****************************************************************************/
VOID DebugMessage ( char dmessage[50] )
   {
   WinMessageBox ( HWND_DESKTOP,
                   HWND_DESKTOP,
                   dmessage,
                   "Debug Message",
                   0,
                   MB_OK            |
                   MB_ERROR         |
                   MB_SYSTEMMODAL   |
                   MB_MOVEABLE );
   }

/*****************************************************************************
* Update the status window                                                   *
*****************************************************************************/
VOID StatusLine ( PMYWINDOWDATA pMyWindowData, CHAR *StatusText )
   {
   WinSetWindowText ( pMyWindowData->StatusWindow, StatusText );
   return;
   }

/*****************************************************************************
* COMMON WINDOWPROC MESSAGE HANDLING                                         *
*                                                                            *
* All of the window procedures will call this module if they do not handle   *
* any given message. This routine will in turn call the default window       *
* procedure ( WinDefWindowProc ) if the message isn't handled here.          *
*****************************************************************************/
MRESULT EXPENTRY ISMCOMMON ( HWND hWnd,
                             ULONG ulMsg,
                             MPARAM mp1,
                             MPARAM mp2 )
   {
   PMYWINDOWDATA  pMyWindowData;
   RECTL          rcl;
   HPS            hps;
   HWND           hFrame;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_INITMYWINDOW:
               DosAllocMem ( (VOID *) &pMyWindowData,
                             sizeof(MYWINDOWDATA),
                             PAG_COMMIT   |
                                 PAG_READ |
                                 PAG_WRITE );
               WinSetWindowPtr ( hWnd, 0, (PVOID) pMyWindowData );
               pMyWindowData->StatusWindow = (HWND) mp1;
               pMyWindowData->MyWindow = hWnd;
               return ( (MRESULT) TRUE );
               break;

      case  WM_CLOSE:
               StatusLine ( pMyWindowData, "" );
               hFrame = WinQueryWindow ( hWnd, QW_PARENT );
               WinDestroyWindow ( hFrame );
               return ( (MRESULT) 0 );
               break;

      case  WM_COMMAND:
            switch ( LONGFROMMP(mp1) )
               {
               case  ID_QUIT:
                     WinPostMsg ( hWnd, WM_CLOSE, 0, 0 );
                     break;

               default:
                     break;
               }
            break;

      case  WM_SETFOCUS:
               if ( pMyWindowData != NULL )
                  StatusLine ( pMyWindowData, "" );
               break;

      case  WM_PAINT:
               hps = WinBeginPaint  ( hWnd, NULLHANDLE, (PRECTL)NULL );
               WinQueryWindowRect   ( hWnd, &rcl );
               WinFillRect          ( hps,  &rcl, SYSCLR_MENU );
               WinEndPaint          ( hps );
               return ( (MRESULT) TRUE );
               break;

      default:
            break;
      }
   return( (MRESULT) WinDefWindowProc ( hWnd, ulMsg, mp1, mp2 )  );
   }

/*****************************************************************************
* WM_TOUCHDOWN: RETURN CODE: TDN_DEFAULT_DELAY                               *
*                                                                            *
* Use the default delay between WM_TOUCHDOWN and WM_BUTTONxDOWN. The result  *
* of this is that if the user touches the sensor and pauses for a duration   *
* equal to the default delay, mouse emulation is performed. However, if the  *
* user touches the sensor and begins a stroke, then gesture recognition is   *
* attempted.                                                                 *
*****************************************************************************/
MRESULT APIENTRY  tdn_default_delay (  HWND     hWnd,
                                       ULONG    ulMsg,
                                       MPARAM   mp1,
                                       MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;
   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );

   switch ( ulMsg )
      {
      case  WM_TOUCHDOWN:
               pMyWindowData->td = TRUE;
               StatusLine ( pMyWindowData, "Returning TDN_DEFAULT_DELAY." );
               return ( (MRESULT) TDN_DEFAULT_DELAY );
               break;

      case  WM_BUTTON1DOWN:
               if ( pMyWindowData->td )
                  {
                  pMyWindowData->td = FALSE;
                  StatusLine ( pMyWindowData, "Mouse Emulation Mode" );
                  };
               return ( (MRESULT) TRUE );
               break;

      case  WM_STROKE:
               pMyWindowData->td = FALSE;
               StatusLine ( pMyWindowData, "Gesture Mode" );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_TOUCHDOWN: RETURN CODE: TDN_IMMEDIATE                                   *
*                                                                            *
* No delay. The program will recieve a  WM_BUTTONxDOWN  message immediately. *
* Note that a stroke buffer is not built when using this return code.        *
*****************************************************************************/
MRESULT APIENTRY  tdn_immediate (  HWND     hWnd,
                                   ULONG    ulMsg,
                                   MPARAM   mp1,
                                   MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;
   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );

   switch ( ulMsg )
      {
      case  WM_TOUCHDOWN:
               StatusLine ( pMyWindowData, "Returning TDN_IMMEDIATE" );
               return ( (MRESULT) TDN_IMMEDIATE );
               break;

      case  WM_BUTTON1DOWN:
               StatusLine ( pMyWindowData, "WM_BUTTON1DOWN has been received" );
               return ( (MRESULT) TRUE );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_TOUCHDOWN: RETURN CODE: TDN_INFINITE                                    *
*                                                                            *
* The program will not recieve a WM_BUTTONxDOWN message as a result of       *
* touchdown.                                                                 *
*****************************************************************************/
MRESULT APIENTRY  tdn_infinite (  HWND     hWnd,
                                  ULONG    ulMsg,
                                  MPARAM   mp1,
                                  MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;
   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_TOUCHDOWN:
               pMyWindowData->td = TRUE;
               StatusLine ( pMyWindowData, "Returning TDN_INFINITE." );
               return ( (MRESULT) TDN_INFINITE );
               break;

      case  WM_BUTTON1DOWN:
               if ( pMyWindowData->td )
                  {
                  StatusLine ( pMyWindowData, "Error: WM_TOUCHDOWN generated WM_BUTTON1DOWN" );
                  };
               return ( (MRESULT) TRUE );
               break;

      case  WM_LIFTOFF:
               pMyWindowData->td = FALSE;
               break;

      case  WM_EXIT_PROXIMITY:
               pMyWindowData->td = FALSE;
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_TOUCHDOWN: RETURN CODE: TDN_INK_STROKE                                  *
*                                                                            *
* Let PenPM do the inking of the stroke for you. The ink is not actually     *
* placed in your window. It is draw in a transparent window that covers the  *
* screen.                                                                    *
*****************************************************************************/
MRESULT APIENTRY  tdn_ink_stroke (  HWND     hWnd,
                                    ULONG    ulMsg,
                                    MPARAM   mp1,
                                    MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_TOUCHDOWN:
               StatusLine ( pMyWindowData, "Returning TDN_INK_STROKE." );
               return ( (MRESULT) TDN_INK_STROKE );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_TOUCHDOWN: RETURN CODE: TDN_NO_INK_STROKE                               *
*                                                                            *
* PenPM will not ink the stroke. This is usually used when the program       *
* intends to do the inking itself.                                           *
*****************************************************************************/
MRESULT APIENTRY  tdn_no_ink_stroke (  HWND     hWnd,
                                       ULONG    ulMsg,
                                       MPARAM   mp1,
                                       MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_TOUCHDOWN:
               StatusLine ( pMyWindowData, "Returning TDN_NO_INK_STROKE." );
               return ( (MRESULT) TDN_NO_INK_STROKE );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_TOUCHDOWN: RETURN CODE: TDN_LEAVE_INK                                   *
*                                                                            *
* Inform PenPM to ink the stroke and leave it on the screen if the stroke    *
* does not leave the window that received WM_TOUCHDOWN. However, if the      *
* stroke extends outside the window then the ink is removed. When your       *
* window repaints, the ink left by PenPM will not be repainted. If you wish  *
* to retain the ink, you must capture the point and/or stroke data and paint *
* the ink yourself.                                                          *
*****************************************************************************/
MRESULT APIENTRY  tdn_leave_ink (   HWND     hWnd,
                                    ULONG    ulMsg,
                                    MPARAM   mp1,
                                    MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_TOUCHDOWN:
               StatusLine ( pMyWindowData, "Returning TDN_LEAVE_INK." );
               return ( (MRESULT) TDN_LEAVE_INK );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_TOUCHDOWN: RETURN CODE: TDN_HIFREQ_MOUSEMOVE                            *
*                                                                            *
* Inform PenPM to report all pointing device movements as WM_MOUSEMOVE's at  *
* full bandwidth. The points are reported in window coordinates. This is     *
* primarily used when all you are doing is inking to the window or display.  *
* If you are inking to a higher resolution bitmap or if you intend to do     *
* some type of recoginition on the strokes then use WM_SENSOR_MOVE since the *
* sensors generally have a higher resolution.                                *
*****************************************************************************/
MRESULT APIENTRY  tdn_hifreq_mousemove (  HWND     hWnd,
                                          ULONG    ulMsg,
                                          MPARAM   mp1,
                                          MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;
   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_TOUCHDOWN:
               pMyWindowData->td = TRUE;
               StatusLine ( pMyWindowData, "Returning TDN_HIFREQ_MOUSEMOVE." );
               return ( (MRESULT) TDN_HIFREQ_MOUSEMOVE );
               break;

      case  WM_MOUSEMOVE:
               if ( pMyWindowData->td )
                  {
                  StatusLine ( pMyWindowData, "WM_MOUSMOVE" );
                  return ( (MRESULT) TRUE );
                  }
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_TOUCHDOWN: RETURN CODE: TDN_SENSOR_MOVE                                 *
*                                                                            *
* Inform PenPM to report all pointing device movements as WM_SENSOR_MOVE's   *
* at sensor resolution. This is usually used when you are inking to a high   *
* resolution bitmap or when you are using the strokes and or points for some *
* type of recoginition. If you are only inking to the display use the        *
* WM_HIGHFREQ_MOUSEMOVE message, since it does not require any scaling of    *
* the point data.                                                            *
*****************************************************************************/
MRESULT APIENTRY  tdn_sensor_move (  HWND     hWnd,
                                     ULONG    ulMsg,
                                     MPARAM   mp1,
                                     MPARAM   mp2   )
   {
   RECTL                    rcl;
   HPS                      hps;
   static HPS               hpsClient;
   static POINTL            points = { 0, 0 } ;
   static BOOL              FirstTime = TRUE;
   static WRTLOCATORDEVINFO WrtLocatorDevInfo;
   WRTEVENTDATA             WrtEventData;
   static SHORT             changecolor = 0;
   static SHORT             numpoints  = 26;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_TOUCHDOWN:
               if ( FirstTime )
                  {
                  WrtEventData = *( (PWRTEVENTDATA) PVOIDFROMMP ( mp2 ) );
                  WrtLocatorDevInfo.cbStructSize = sizeof ( WRTLOCATORDEVINFO );
                  WrtQueryLocatorCaps ( &WrtEventData.ulLocatorID, &WrtLocatorDevInfo );
                  FirstTime=FALSE;
                  };
               WinSetCapture ( HWND_DESKTOP, hWnd );
               hpsClient = WinGetPS ( hWnd );
               points.x = SHORT1FROMMP ( mp1 );
               points.y = SHORT2FROMMP ( mp1 );
               GpiMove ( hpsClient, &points );
               GpiSetColor ( hpsClient, CLR_BLUE );
               numpoints = 25;
               StatusLine ( pMyWindowData, "Returning TDN_INFINITE | TDN_SENSOR_MOVE | TDN_NO_INK_STROKE." );
               return ( (MRESULT) ( TDN_INFINITE | TDN_SENSOR_MOVE | TDN_NO_INK_STROKE ) );
               break;

      case  WM_SENSOR_MOVE:
               points.x = SHORT1FROMMP ( mp1 );
               points.y = SHORT2FROMMP ( mp1 );
               WrtMapPointLong ( hWnd,
                                 &points,
                                 MP_SCALE,
                                 WrtLocatorDevInfo.ulSensorXpts,
                                 WrtLocatorDevInfo.ulSensorYpts,
                                 0L,
                                 0L,
                                 1 );
               GpiLine ( hpsClient, &points );
               numpoints--;
               if ( numpoints == 0 )
                  {
                  if ( changecolor )
                     {
                     changecolor = 0;
                     GpiSetColor ( hpsClient, CLR_BLUE );
                     }
                  else
                     {
                     changecolor = 1;
                     GpiSetColor ( hpsClient, CLR_RED );
                     };
                  numpoints=25;
                  };
               GpiMove ( hpsClient, &points );
               return ( (MRESULT) 0 );
               break;

      case  WM_LIFTOFF:
               WinSetCapture ( HWND_DESKTOP, NULLHANDLE );
               points.x = SHORT1FROMMP ( mp1 );
               points.y = SHORT2FROMMP ( mp1 );
               GpiLine ( hpsClient, &points );
               WinReleasePS ( hpsClient );
               return ( (MRESULT) LO_STROKE_PROCESSED );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_TOUCHDOWN: RETURN CODE: TDN_IMMEDIATE |                                 *
*                            TDN_HEFREQ_MOUSEMOVE |                          *
*                            TDN_NO_INK_STROKE                               *
*                                                                            *
* This is an example of how to use ink without a stroke buffer.              *
*****************************************************************************/
MRESULT APIENTRY  ink_without_stroke_buffer (  HWND     hWnd,
                                               ULONG    ulMsg,
                                               MPARAM   mp1,
                                               MPARAM   mp2   )
   {
   HPS            hps;
   static HPS     hpsClient;
   static POINTL  points = { 0, 0 } ;
   static RECTL   rcl;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_TOUCHDOWN:
               pMyWindowData->td = TRUE;
               hpsClient = WinGetPS ( hWnd );

               WinSetCapture ( HWND_DESKTOP, hWnd );
               points.x = SHORT1FROMMP ( mp1 );
               points.y = SHORT2FROMMP ( mp1 );
               GpiMove ( hpsClient, &points );
               WinQueryWindowRect   ( hWnd, &rcl );


               StatusLine ( pMyWindowData, "Returning TDN_IMMEDIATE | TDN_HIFREQ_MOUSEMOVE | TDN_NO_INK_STROKE." );
               return ( (MRESULT) ( TDN_IMMEDIATE        |
                                    TDN_HIFREQ_MOUSEMOVE |
                                    TDN_NO_INK_STROKE ));
               break;

      case  WM_MOUSEMOVE:
               if ( pMyWindowData->td )
                  {
                  /**********************************************************
                  * Make sure that the sign is preserved by casting it to a *
                  **********************************************************/
                  points.x = (SHORT) SHORT1FROMMP ( mp1 );
                  points.y = (SHORT) SHORT2FROMMP ( mp1 );

                  GpiLine ( hpsClient, &points );
                  GpiMove ( hpsClient, &points );
                  };
               break;

      case  WM_LIFTOFF:
               WinSetCapture ( HWND_DESKTOP, NULLHANDLE );
               pMyWindowData->td = FALSE;
               GpiMove ( hpsClient, &points );
               points.x = SHORT1FROMMP ( mp1 );
               points.y = SHORT2FROMMP ( mp1 );
               GpiLine ( hpsClient, &points );

               WinReleasePS ( hpsClient );

               return ( (MRESULT) LO_DEFAULT );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_TOUCHDOWN: RETURN CODE: TDN_INFINITE  |                                 *
*                            TDN_SENSOR_MOVE |                               *
*                            TDN_LEAVE_INK                                   *
*                                                                            *
* This is an example of how to use ink with a stroke buffer.                 *
*****************************************************************************/
MRESULT APIENTRY  ink_with_stroke_buffer  ( HWND     hWnd,
                                            ULONG    ulMsg,
                                            MPARAM   mp1,
                                            MPARAM   mp2   )
   {
   RECTL          rcl;
   HPS            hps;
   ULONG          ulBufLen;
   PSTROKEDATA    pStrokeData;
   ULONG          ulConversionOptions;
   ULONG          ulXOutput;
   ULONG          ulYOutput;
   ULONG          ulAuxData;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_TOUCHDOWN:
               StatusLine ( pMyWindowData, "Returning TDN_INFINITE | TDN_SENSOR_MOVE | TDN_LEAVE_INK" );
               WinSetCapture ( HWND_DESKTOP, hWnd );
               return ( (MRESULT) ( TDN_INFINITE    |
                                    TDN_SENSOR_MOVE |
                                    TDN_LEAVE_INK ));
               break;

      case  WM_LIFTOFF:

               WinSetCapture ( HWND_DESKTOP, NULLHANDLE );

               /***************************************************************
               * Get the stroke data size                                     *
               ***************************************************************/
               pStrokeData          = NULL;
               ulBufLen             = 0;
               ulConversionOptions  = QSD_SCALE;
               ulXOutput            = 0;
               ulYOutput            = 0;
               ulAuxData            = 0;
               if ( !WrtQueryStrokeData ( (PBYTE) pStrokeData,
                                          &ulBufLen,
                                          hWnd,
                                          ulConversionOptions,
                                          ulXOutput,
                                          ulYOutput,
                                          ulAuxData ) )
                  {
                  /************************************************************
                  * Check to verify that buffer length is net set to zero     *
                  ************************************************************/
                  if ( !ulBufLen )
                     {
                     StatusLine ( pMyWindowData,"Zero Buffer Length");
                     return((MPARAM)FALSE);
                     };

                  /************************************************************
                  * Allocate the memory for the stroke data                   *
                  ************************************************************/
                  DosAllocMem ( (VOID *) &pStrokeData,
                                ulBufLen,
                                PAG_COMMIT   |
                                    PAG_READ |
                                    PAG_WRITE );
                  if ( pStrokeData == NULL )
                     {
                     StatusLine ( pMyWindowData,"DosAllocMem Failed");
                     return((MPARAM)FALSE);
                     };

                  /************************************************************
                  * Initialize the structure                                  *
                  ************************************************************/
                  pStrokeData->cbStructSize = sizeof ( STROKEDATA );
                  if ( !WrtQueryStrokeData ( (PBYTE) pStrokeData,
                                             &ulBufLen,
                                             hWnd,
                                             ulConversionOptions,
                                             ulXOutput,
                                             ulYOutput,
                                             ulAuxData ) )
                     {
                     hps = WinGetPS ( hWnd );
                     GpiSetColor ( hps, CLR_BLUE );
                     GpiMove ( hps,
                               pStrokeData->pXY );
                     GpiPolyLine( hps,
                                  pStrokeData->ulNumPoints - 1L,
                                  pStrokeData->pXY++ );
                     WinReleasePS ( hps );
                     };
                  DosFreeMem ( (VOID *) pStrokeData );
                  };

               return ( (MRESULT) LO_STROKE_PROCESSED );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_TOUCHDOWN: RETURN CODE: TDN_DEFAULT_DELAY |                             *
*                            TDN_INK_STROKE                                  *
*                                                                            *
* This is default ink processing.                                            *
*****************************************************************************/
MRESULT APIENTRY  default_inking     ( HWND     hWnd,
                                       ULONG    ulMsg,
                                       MPARAM   mp1,
                                       MPARAM   mp2   )
   {
   RECTL       rcl;
   static      HPS         hps;
   static      HPOINTER    hptrOld;
   HPOINTER                hptrNew;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_TOUCHDOWN:
               StatusLine ( pMyWindowData, "Returning TDN_DEFAULT_DELAY | TDN_INK_STROKE" );
               WinSetCapture ( HWND_DESKTOP, hWnd );
               return ( (MRESULT) ( TDN_DEFAULT_DELAY |
                                    TDN_INK_STROKE      ) );
               break;

      case  WM_PAUSE_TIMEOUT:
               hptrNew = WinQuerySysPointer ( HWND_DESKTOP,
                                              SPTR_ILLEGAL,
                                              FALSE );
               hptrOld = WinQueryPointer ( HWND_DESKTOP );
               WinSetPointer ( HWND_DESKTOP, hptrNew );
               break;

      case  WM_LIFTOFF:
               WinSetPointer ( HWND_DESKTOP, hptrOld );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_LIFTOFF: RETURN CODE: LO_DEFAULT                                        *
*                                                                            *
* The default behavior is to continue with gesture recognition and send a    *
* WM_STROKE and WM_RECO messages.                                            *
*****************************************************************************/
MRESULT APIENTRY  lo_default (  HWND     hWnd,
                                ULONG    ulMsg,
                                MPARAM   mp1,
                                MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_LIFTOFF:
               StatusLine ( pMyWindowData, "Returning LO_DEFAULT" );
               return ( (MRESULT) LO_DEFAULT );
               break;

      case  WM_STROKE:
               StatusLine ( pMyWindowData, "WM_STROKE received");
               return ( (MRESULT) TRUE );
               break;

      case  WM_RECO:
               StatusLine ( pMyWindowData, "WM_RECO received");
               return ( (MRESULT) TRUE );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_LIFTOFF: RETURN CODE: LO_STROKE_PROCESSED                               *
*                                                                            *
* Inform PenPM that the stroke has been processed and to not perform any     *
* furthur processing on the stroke.
*****************************************************************************/
MRESULT APIENTRY  lo_stroke_processed (  HWND     hWnd,
                                         ULONG    ulMsg,
                                         MPARAM   mp1,
                                         MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_LIFTOFF:
               StatusLine ( pMyWindowData, "Returning LO_STROKE_PROCESSED" );
               return ( (MRESULT) LO_STROKE_PROCESSED );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_LIFTOFF: RETURN CODE: LO_EMULATE_MOUSE                                  *
*                                                                            *
* Inform PenPM to send the mouse button down and up messages.                *
*****************************************************************************/
MRESULT APIENTRY  lo_emulate_mouse (  HWND     hWnd,
                                      ULONG    ulMsg,
                                      MPARAM   mp1,
                                      MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;
   int         b1d;
   int         b1u;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_LIFTOFF:
               StatusLine ( pMyWindowData, "Returning LO_EMULATE_MOUSE" );
               return ( (MRESULT) LO_EMULATE_MOUSE );
               break;

      case  WM_BUTTON1DOWN:
               StatusLine ( pMyWindowData, "WM_BUTTON1DOWN" );
               b1d++;
               break;

      case  WM_BUTTON1UP:
               b1u++;
               StatusLine ( pMyWindowData, "WM_BUTTON1UP" );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_STROKE: RETURN CODE: STK_DEFAULT                                        *
*                                                                            *
* Application does not process stroke. Informs PenPM to do gesture           *
* recognition processing.                                                    *
*****************************************************************************/
MRESULT APIENTRY  stk_default (  HWND     hWnd,
                                 ULONG    ulMsg,
                                 MPARAM   mp1,
                                 MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_STROKE:
               StatusLine ( pMyWindowData, "Returning STK_DEFAULT" );
               return ( (MRESULT) STK_DEFAULT );
               break;

      case  WM_RECO:
               StatusLine ( pMyWindowData, "WM_RECO received");
               return ( (MRESULT) TRUE );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_STROKE: RETURN CODE: STK_STROKE_PROCESSED                               *
*                                                                            *
* Inform PenPM that the application has already processed the stroke and     *
* thus do not continue with gesture recognition processing.                  *
*****************************************************************************/
MRESULT APIENTRY  stk_stroke_processed (  HWND     hWnd,
                                          ULONG    ulMsg,
                                          MPARAM   mp1,
                                          MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_STROKE:
               StatusLine ( pMyWindowData, "Returning STK_STROKE_PROCESSED" );
               return ( (MRESULT) STK_STROKE_PROCESSED );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_SENSOR_MOVE: RETURN: VOID                                               *
*                                                                            *
* Inform PenPM at WM_TOUCHDOWN to send us all points as WM_SENSOR_MOVE's     *
* with sensor resolution. Note that this does not mean that WM_MOUSEMOVE's   *
* will not be sent. WM_MOUSEMOVE's are always being sent around PM to move   *
* mouse pointer. WM_SENSOR_MOVE's at sensor resolution are usually much      *
* better for things like gesture and handwriting recognition processing.     *
*****************************************************************************/
MRESULT APIENTRY  wm_sensor_move (  HWND     hWnd,
                                    ULONG    ulMsg,
                                    MPARAM   mp1,
                                    MPARAM   mp2   )
   {
   RECTL                    rcl;
   HPS                      hps;
   static HPS               hpsClient;
   static POINTL            points = { 0, 0 } ;
   static BOOL              FirstTime = TRUE;
   static WRTLOCATORDEVINFO WrtLocatorDevInfo;
   WRTEVENTDATA             WrtEventData;
   static SHORT             changecolor = 0;
   static SHORT             numpoints  = 26;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_TOUCHDOWN:
               if ( FirstTime )
                  {
                  WrtEventData = *( (PWRTEVENTDATA) PVOIDFROMMP ( mp2 ) );
                  WrtLocatorDevInfo.cbStructSize = sizeof ( WRTLOCATORDEVINFO );
                  WrtQueryLocatorCaps ( &WrtEventData.ulLocatorID, &WrtLocatorDevInfo );
                  FirstTime=FALSE;
                  };
               WinSetCapture ( HWND_DESKTOP, hWnd );
               hpsClient = WinGetPS ( hWnd );
               points.x = SHORT1FROMMP ( mp1 );
               points.y = SHORT2FROMMP ( mp1 );
               GpiMove ( hpsClient, &points );
               GpiSetColor ( hpsClient, CLR_BLUE );
               numpoints = 25;
               StatusLine ( pMyWindowData, "Returning TDN_INFINITE | TDN_SENSOR_MOVE | TDN_NO_INK_STROKE." );
               return ( (MRESULT) ( TDN_INFINITE | TDN_SENSOR_MOVE | TDN_NO_INK_STROKE ) );
               break;

      case  WM_SENSOR_MOVE:
               points.x = SHORT1FROMMP ( mp1 );
               points.y = SHORT2FROMMP ( mp1 );
               WrtMapPointLong ( hWnd,
                                 &points,
                                 MP_SCALE,
                                 WrtLocatorDevInfo.ulSensorXpts,
                                 WrtLocatorDevInfo.ulSensorYpts,
                                 0L,
                                 0L,
                                 1 );
               GpiLine ( hpsClient, &points );
               numpoints--;
               if ( numpoints == 0 )
                  {
                  if ( changecolor )
                     {
                     changecolor = 0;
                     GpiSetColor ( hpsClient, CLR_BLUE );
                     }
                  else
                     {
                     changecolor = 1;
                     GpiSetColor ( hpsClient, CLR_RED );
                     };
                  numpoints=25;
                  };
               GpiMove ( hpsClient, &points );
               return ( (MRESULT) 0 );
               break;

      case  WM_LIFTOFF:
               WinSetCapture ( HWND_DESKTOP, NULLHANDLE );
               points.x = SHORT1FROMMP ( mp1 );
               points.y = SHORT2FROMMP ( mp1 );
               GpiLine ( hpsClient, &points );
               WinReleasePS ( hpsClient );
               return ( (MRESULT) LO_STROKE_PROCESSED );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_TAP: RETURN: TP_DEFAULT                                                 *
*                                                                            *
* Tell PenPM to perform mouse emulation, ie send WM_BUTTON1DOWN and          *
* WM_BUTTON1UP messages. However, if you return TDN_INFINITE at WM_TOUCHDOWN *
* the button events will not be sent.                                        *
*****************************************************************************/
MRESULT APIENTRY  tap_tp_default (  HWND     hWnd,
                                    ULONG    ulMsg,
                                    MPARAM   mp1,
                                    MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;
   static int  tap = FALSE;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_TAP:
               tap = TRUE;
               StatusLine ( pMyWindowData, "Returning TP_DEFAULT. ");
               return ( (MRESULT) TP_DEFAULT );
               break;

      case  WM_BUTTON1DOWN:
               if ( tap )
                  {
                  StatusLine ( pMyWindowData, "Mouse emulation" );
                  tap = FALSE;
                  };
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_TAP: RETURN: TP_TAP_PROCESSED                                           *
*                                                                            *
* Inform PenPM that the application has processed the WM_TAP message and     *
* take no furthur action in processing the event.                            *
*****************************************************************************/
MRESULT APIENTRY  tap_tp_tap_processed (  HWND     hWnd,
                                          ULONG    ulMsg,
                                          MPARAM   mp1,
                                          MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;
   static int  tap = FALSE;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_TAP:
               tap = TRUE;
               StatusLine ( pMyWindowData, "Returning TP_TAP_PROCESSED. ");
               return ( (MRESULT) TP_TAP_PROCESSED );
               break;

      case  WM_BUTTON1DOWN:
               if ( tap )
                  {
                  StatusLine ( pMyWindowData, "Mouse emulation" );
                  tap = FALSE;
                  };
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_DBL_TAP: RETURN: TP_DEFAULT                                             *
*                                                                            *
* Tell PenPM to perform mouse emulation, ie send WM_BUTTON1DOWN and          *
* WM_BUTTON1UP messages. However, if you return TDN_INFINITE at WM_TOUCHDOWN *
* the button events will not be sent.                                        *
*****************************************************************************/
MRESULT APIENTRY  dbl_tap_tp_default (  HWND     hWnd,
                                        ULONG    ulMsg,
                                        MPARAM   mp1,
                                        MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;
   static int  tap = FALSE;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_DBL_TAP:
               tap = TRUE;
               StatusLine ( pMyWindowData, "Returning TP_DEFAULT. ");
               return ( (MRESULT) TP_DEFAULT );
               break;

      case  WM_BUTTON1DOWN:
               if ( tap )
                  {
                  StatusLine ( pMyWindowData, "Mouse emulation." );
                  tap = FALSE;
                  };
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_DBL_TAP: RETURN: TP_TAP_PROCESSED                                       *
*                                                                            *
* Inform PenPM that the application has processed the WM_TAP message and     *
* take no furthur action in processing the event.                            *
*****************************************************************************/
MRESULT APIENTRY  dbl_tap_tp_tap_processed (  HWND     hWnd,
                                              ULONG    ulMsg,
                                              MPARAM   mp1,
                                              MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;
   static int  tap = FALSE;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_DBL_TAP:
               tap = TRUE;
               StatusLine ( pMyWindowData, "Returning TP_TAP_PROCESSED. ");
               return ( (MRESULT) TP_TAP_PROCESSED );
               break;

      case  WM_BUTTON1DOWN:
               if ( tap )
                  {
                  tap = FALSE;
                  StatusLine ( pMyWindowData, "Mouse emulation." );
                  };
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_PAUSE_TIMEOUT: RETURN: PTO_DEFAULT                                      *
*                                                                            *
* This message is generated by PenPM when the user has touched the sensor    *
* and remained motionless for a period of time. PTO_DEFAULT tells PenPM to   *
* allow mouse emulation.                                                     *
*****************************************************************************/
MRESULT APIENTRY  pto_default (  HWND     hWnd,
                                 ULONG    ulMsg,
                                 MPARAM   mp1,
                                 MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;
   static BOOL pto = FALSE;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_PAUSE_TIMEOUT:
               pto = TRUE;
               StatusLine ( pMyWindowData, "Returning PTO_DEFAULT. ");
               return ( (MRESULT) PTO_DEFAULT );
               break;

      case  WM_BUTTON1DOWN:
               if ( pto )
                  {
                  StatusLine ( pMyWindowData, "Mouse emulation" );
                  pto = FALSE;
                  };
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_PAUSE_TIMEOUT: RETURN: PTO_CONTINUE                                     *
*                                                                            *
* This message is generated by PenPM when the user has touched the sensor    *
* and remained motionless for a period of time. PTO_CONTINUE tells PenPM to  *
* remain in a gesture mode and not perform mouse emulation.                  *
*****************************************************************************/
MRESULT APIENTRY  pto_continue (  HWND     hWnd,
                                  ULONG    ulMsg,
                                  MPARAM   mp1,
                                  MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_PAUSE_TIMEOUT:
               StatusLine ( pMyWindowData, "Returning PTO_CONTINUE. ");
               return ( (MRESULT) PTO_CONTINUE );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_EXIT_PROXIMITY: RETURN: VOID                                            *
*                                                                            *
* This indicates that the pointing device has left proximity to the sensor.  *
* Note that not all devices provide proximity data.                          *
*****************************************************************************/
MRESULT APIENTRY  wm_exit_proximity (  HWND     hWnd,
                                       ULONG    ulMsg,
                                       MPARAM   mp1,
                                       MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_EXIT_PROXIMITY:
               StatusLine ( pMyWindowData, "Exiting Proximity" );
               return ( (MRESULT) 0 );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_WRT_DEV_CAPS_CHANGE: RETURN: VOID                                       *
*                                                                            *
* This message indicates a change to the device capabilities has been        *
* reported to PenPM.                                                         *
*****************************************************************************/
MRESULT APIENTRY  wm_wrt_dev_caps_change (  HWND     hWnd,
                                            ULONG    ulMsg,
                                            MPARAM   mp1,
                                            MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_WRT_DEV_CAPS_CHANGE:
               StatusLine ( pMyWindowData, "Device capabilities changed" );
               return ( (MRESULT) 0 );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* WM_WRT_SYSVALUECHANGED: RETURN: VOID                                       *
*                                                                            *
* This message indicates that a change to the PenPM system variables has     *
* occurred. Note that it is the responsibility of the program changing the   *
* value to broadcaset this message to the rest of the system.                *
*****************************************************************************/
MRESULT APIENTRY  wm_wrt_sysvaluechanged (  HWND     hWnd,
                                            ULONG    ulMsg,
                                            MPARAM   mp1,
                                            MPARAM   mp2   )
   {
   RECTL       rcl;
   HPS         hps;

   PMYWINDOWDATA  pMyWindowData;

   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_WRT_SYSVALUECHANGED:
               StatusLine ( pMyWindowData, "System value changed" );
               return ( (MRESULT) 0 );
               break;

      default:
               break;
      };
   return( (MRESULT) ISMCOMMON ( hWnd, ulMsg, mp1, mp2 )  );
   };

/*****************************************************************************
* This is an example of inking and editing of ink using the capabilities of  *
* the graphics engine, provided with OS/2 2.1. You enter ink while in a      *
* freehand mode. The ink is grouped into sets of strokes from the moment of  *
* touchdown until the sensor leaves proximity. You can change the color of   *
* an entire group with a C-tap gesture. You can delete an entire group with  *
* an X gesture. You can delete any individual stroke with a pigtail gesture. *
*****************************************************************************/
MRESULT APIENTRY InkEditing (  HWND     hWnd,
                               ULONG    ulMsg,
                               MPARAM   mp1,
                               MPARAM   mp2   )
{
   RECTL          rcl;
   SIZEL          PickSize;
   SHORT          NumHits;
   SIZEL          PageSize;
   HWND           hMenu;
   MENUITEM       menuitem;
   PMYWINDOWDATA  pMyWindowData;
   HRGN           hrgn;
   HRGN           hrgnold;
   HPS            hpspaint;
   LINEWIDTHORDER LineWidthOrder;
   SHORT          i;

   PickSize.cx = 10;
   PickSize.cy = 10;
   pMyWindowData = (PMYWINDOWDATA) WinQueryWindowPtr ( hWnd, 0 );
   switch ( ulMsg )
      {
      case  WM_INITMYWINDOW:

               /**************************************************************
               * Add Pen input mode menu item                                *
               **************************************************************/
               hMenu = WinWindowFromID ( WinQueryWindow ( hWnd, QW_PARENT ),
                                         FID_MENU );
               menuitem.iPosition   = MIT_END;
               menuitem.afStyle     = MIS_TEXT;
               menuitem.afAttribute = 0;
               menuitem.id          = ID_MENUITEM;
               menuitem.hItem       = 0;
               menuitem.hwndSubMenu = 0;

               WinSendMsg ( hMenu,
                            MM_INSERTITEM,
                            (MPARAM) &menuitem,
                            (MPARAM) "GestureMode" );

               /**************************************************************
               * Initialize window instance data                             *
               **************************************************************/
               DosAllocMem ( (VOID *) &pMyWindowData,
                             sizeof(MYWINDOWDATA),
                             PAG_COMMIT   |
                                 PAG_READ |
                                 PAG_WRITE );
               pMyWindowData->MyWindow = hWnd;
               pMyWindowData->StatusWindow   = (HWND) mp1;
               pMyWindowData->freehand       = TRUE;
               pMyWindowData->NewSegment     = TRUE;
               pMyWindowData->SegmentName    = 1;
               pMyWindowData->ElementName    = 0;
               if ( !WinSetWindowPtr ( hWnd, 0, (PVOID) pMyWindowData ) )
                  return ( (MPARAM) FALSE );

               /**************************************************************
               * Create the presentation space for the window                *
               **************************************************************/
               pMyWindowData->hab = WinQueryAnchorBlock ( hWnd );
               pMyWindowData->hdc = WinOpenWindowDC ( hWnd );
               DevQueryCaps ( pMyWindowData->hdc, CAPS_WIDTH, 1L, &PageSize.cx );
               DevQueryCaps ( pMyWindowData->hdc, CAPS_WIDTH, 1L, &PageSize.cy );
               pMyWindowData->hps = GpiCreatePS ( pMyWindowData->hab,
                                                  pMyWindowData->hdc,
                                                  &PageSize,
                                                  ( PU_PELS      |
                                                    GPIT_NORMAL  |
                                                    GPIF_LONG    |
                                                    GPIA_ASSOC   ) );
               if ( pMyWindowData->hps == GPI_ERROR )
                  {
                  StatusLine ( pMyWindowData, "Could not create PS" );
                  WinPostMsg ( hWnd, WM_CLOSE, (MPARAM) 0, (MPARAM) 0 );
                  };

               /**************************************************************
               * Prepare the presentation space for drawing and retaining    *
               * the graphic primatives                                      *
               **************************************************************/
               GpiSetDrawingMode         ( pMyWindowData->hps,
                                           DM_DRAWANDRETAIN );
               GpiSetInitialSegmentAttrs ( pMyWindowData->hps,
                                           ATTR_FASTCHAIN,
                                           ATTR_OFF );
               GpiSetDrawControl         ( pMyWindowData->hps,
                                           DCTL_ERASE,
                                           DCTL_OFF );
               GpiSetDrawControl         ( pMyWindowData->hps,
                                           DCTL_CORRELATE,
                                           DCTL_ON );
               GpiSetDrawControl         ( pMyWindowData->hps,
                                           DCTL_BOUNDARY,
                                           DCTL_OFF );
               GpiSetInitialSegmentAttrs ( pMyWindowData->hps,
                                           ATTR_DETECTABLE,
                                           ATTR_OFF );
               pMyWindowData->points.x = 0;
               pMyWindowData->points.y = 0;
               GpiMove ( pMyWindowData->hps, &pMyWindowData->points );
               pMyWindowData->points.x = PageSize.cx;
               pMyWindowData->points.y = PageSize.cy;
               GpiOpenSegment  ( pMyWindowData->hps,
                                 pMyWindowData->SegmentName );
               GpiSetColor ( pMyWindowData->hps, CLR_PALEGRAY );
               GpiBox ( pMyWindowData->hps, DRO_FILL, &pMyWindowData->points, 0L, 0L );
               GpiCloseSegment ( pMyWindowData->hps );
               pMyWindowData->CurrentColor = InkColor[0];
               GpiSetColor ( pMyWindowData->hps, pMyWindowData->CurrentColor );
               GpiSetInitialSegmentAttrs ( pMyWindowData->hps,
                                           ATTR_DETECTABLE,
                                           ATTR_ON );

               WinShowWindow ( hWnd, TRUE );

               pMyWindowData->InkColors = CreateInkControls ( hWnd );
               GpiSetPickApertureSize ( pMyWindowData->hps, PICKAP_REC, &PickSize );

               WinSetFocus     ( HWND_DESKTOP, hWnd );

               StatusLine ( pMyWindowData, "Freehand Mode" );
               return((MRESULT) TRUE);
               break;

      /***********************************************************************
      * Process the help request                                             *
      ***********************************************************************/
      case  WM_PROCESSDIALOG:
               WinDlgBox (  HWND_DESKTOP,
                            hWnd,
                            InkEditHelp,
                            NULLHANDLE,
                            ID_INKEDITGESTUREHELP,
                            NULL );
               return((MRESULT) TRUE);
               break;

      /***********************************************************************
      * Process the menu selections                                          *
      ***********************************************************************/
      case  WM_COMMAND:
               switch ( SHORT1FROMMP (mp1) )
                  {
                  case  ID_QUIT:
                        WinPostMsg ( hWnd, WM_CLOSE, 0, 0 );
                        break;

                  case  ID_MENUITEM:
                           pMyWindowData->freehand = FALSE;
                           StatusLine ( pMyWindowData, "Gesture Mode - ? for Help" );
                           break;
                  default:
                        break;
                  };
               break;

      /***********************************************************************
      * When we get focus, update the status line to indicate the mode this  *
      * window is in.                                                        *
      ***********************************************************************/
      case  WM_SETFOCUS:
               if ( pMyWindowData != NULL )
                  {
                  if ( pMyWindowData->freehand )
                     {
                     StatusLine ( pMyWindowData, "Freehand Mode" );
                     }
                  else
                     {
                     StatusLine ( pMyWindowData, "Gesture Mode - ? for Help" );
                     };
                  };
               break;

      /***********************************************************************
      * Set the current color to the value selected in the ink colors        *
      * value set control.                                                   *
      ***********************************************************************/
      case  WM_CONTROL:
               switch ( SHORT2FROMMP(mp1) )
                  {
                  case  VN_SELECT:
                        switch ( SHORT1FROMMP(mp1) )
                        {
                           case  ID_VALUE_SET_INK:
                                 pMyWindowData->CurrentColor = InkColor[SHORT1FROMMP ( mp2 )-1];
                                 break;

                           default:
                                 break;
                        };
                        return( (MPARAM)TRUE );
                        break;

                  default:
                        break;
                  };
               break;

      /***********************************************************************
      * The use has touched the sensor. If this is to be a new segment, then *
      * setup the segment attributes for color, width, lineend, and linejoin.*
      * Then begin a new stroke. The importent thing to do here is set the   *
      * tag for the element and set a label with the same value for later    *
      * locating this point.                                                 *
      ***********************************************************************/
      case  WM_TOUCHDOWN:
               if ( pMyWindowData->freehand )
                  {
                  pMyWindowData->SegmentName++;
                  if ( pMyWindowData->NewSegment )
                     {
                     pMyWindowData->ElementName = 0;
                     pMyWindowData->NewSegment  = FALSE;
                     GpiOpenSegment      ( pMyWindowData->hps,
                                           pMyWindowData->SegmentName );
                     GpiLabel            ( pMyWindowData->hps,
                                           LABEL_BEGIN_SEGMENT );

                     GpiLabel            ( pMyWindowData->hps,
                                           LABEL_BEGIN_COLOR );
                     GpiSetColor         ( pMyWindowData->hps,
                                           pMyWindowData->CurrentColor );
                     GpiLabel            ( pMyWindowData->hps,
                                           LABEL_END_COLOR );

                     GpiLabel            ( pMyWindowData->hps,
                                           LABEL_BEGIN_WIDTH );
                     GpiSetLineWidthGeom ( pMyWindowData->hps, 1L );
                     GpiLabel            ( pMyWindowData->hps,
                                           LABEL_END_WIDTH );

                     GpiSetLineEnd       ( pMyWindowData->hps, LINEEND_ROUND );
                     GpiSetLinejOIN      ( pMyWindowData->hps, LINEJOIN_ROUND );

                     };
                  pMyWindowData->ElementName++;
                  GpiSetTag       ( pMyWindowData->hps,
                                    pMyWindowData->ElementName );
                  GpiLabel        ( pMyWindowData->hps,
                                    pMyWindowData->ElementName );
                  GpiLabel        ( pMyWindowData->hps,
                                    LABEL_BEGIN_STROKE );
                  GpiLabel        ( pMyWindowData->hps,
                                    LABEL_BEGIN_BEGIN_PATH );
                  GpiLabel        ( pMyWindowData->hps,
                                    LABEL_END_BEGIN_PATH );

                  pMyWindowData->td = TRUE;
                  WinSetCapture ( HWND_DESKTOP, hWnd );
                  pMyWindowData->points.x = SHORT1FROMMP ( mp1 );
                  pMyWindowData->points.y = SHORT2FROMMP ( mp1 );
                  GpiMove ( pMyWindowData->hps, &pMyWindowData->points );
                  return ( (MRESULT) ( TDN_IMMEDIATE        |
                                       TDN_HIFREQ_MOUSEMOVE |
                                       TDN_NO_INK_STROKE ));
                  }
               else
                  {
                  return((MRESULT) ( TDN_DEFAULT_DELAY | TDN_INK_STROKE ));
                  };
               break;

      /***********************************************************************
      * In this case we are using the hifrequency mouse moves to do the      *
      * inking. We could have just as easily used sensor moves if we desired *
      * a higher resolution.                                                 *
      ***********************************************************************/
      case  WM_MOUSEMOVE:
               if ( pMyWindowData->td )
                  {

                  /**********************************************************
                  * Make sure that the sign is preserved by casting it to a *
                  * short                                                   *
                  **********************************************************/
                  pMyWindowData->points.x = (SHORT) SHORT1FROMMP ( mp1 );
                  pMyWindowData->points.y = (SHORT) SHORT2FROMMP ( mp1 );

                  GpiLine ( pMyWindowData->hps, &pMyWindowData->points );
                  };
               break;

      /***********************************************************************
      * When the user lifts the pointing device off of the sensor, we will   *
      * finish the stroke and set the labels for later editing.              *
      ***********************************************************************/
      case  WM_LIFTOFF:
               if ( pMyWindowData->td )
                  {
                  WinSetCapture ( HWND_DESKTOP, NULLHANDLE );
                  pMyWindowData->td = FALSE;
                  pMyWindowData->points.x = SHORT1FROMMP ( mp1 );
                  pMyWindowData->points.y = SHORT2FROMMP ( mp1 );
                  GpiLine ( pMyWindowData->hps, &pMyWindowData->points );
                  GpiLabel ( pMyWindowData->hps, LABEL_BEGIN_END_PATH );
                  GpiLabel ( pMyWindowData->hps, LABEL_END_END_PATH );
                  GpiLabel ( pMyWindowData->hps, LABEL_END_STROKE );
                  return ( (MRESULT) LO_STROKE_PROCESSED );
                  }
               else
                  {
                  return ( (MRESULT) LO_DEFAULT );
                  };
               break;

      /***********************************************************************
      * Close the segment when the pointer device leaves proximity.          *
      ***********************************************************************/
      case  WM_EXIT_PROXIMITY:
               pMyWindowData->NewSegment  = TRUE;
               pMyWindowData->ElementName = 0;
               GpiLabel        ( pMyWindowData->hps, LABEL_END_SEGMENT );
               GpiCloseSegment ( pMyWindowData->hps );
               break;

      /***********************************************************************
      * When in a gesture mode, the reco engine will inform us of any reco   *
      * event. mp1 contains the hotspot of the gesture. mp2 has a pointer to *
      * the reco event data.                                                 *
      ***********************************************************************/
      case  WM_RECO:
               pMyWindowData->HotSpot.x = (SHORT) SHORT1FROMMP ( mp1 );
               pMyWindowData->HotSpot.y = (SHORT) SHORT2FROMMP ( mp1 );
               pMyWindowData->RecoData  = *((PRECODATA) PVOIDFROMMP ( mp2 ));
               switch ( pMyWindowData->RecoData.virtual_id )
                  {

                  /***********************************************************
                  * By default a ? gesture is mapped to the virtual id for   *
                  * help. Pop up a dialog box with the help information.     *
                  ***********************************************************/
                  case  VE_HELP:
                        WinPostMsg ( hWnd, WM_PROCESSDIALOG, (MPARAM)0, (MPARAM)0 );
                        return( (MRESULT) RECO_PROCESSED );
                        break;

                  /***********************************************************
                  * This event is mapped to the scratchout gesture. We will  *
                  * use this to delete all segments contained within the     *
                  * bounding box for the gesture.                            *
                  ***********************************************************/
                  case  VE_SCRATCHOUT:
                        PickSize.cx = pMyWindowData->RecoData.bound_box.xRight - pMyWindowData->RecoData.bound_box.xLeft;
                        PickSize.cy = pMyWindowData->RecoData.bound_box.yTop - pMyWindowData->RecoData.bound_box.yBottom;
                        GpiSetPickApertureSize ( pMyWindowData->hps, PICKAP_REC, &PickSize );
                        NumHits = GpiCorrelateChain ( pMyWindowData->hps,
                                                      PICKSEL_VISIBLE,
                                                      &pMyWindowData->HotSpot,
                                                      10L,
                                                      1L,
                                                      &pMyWindowData->SegmentHits[0].Segment );
                        if ( NumHits )
                        {
                           hrgn = CreateClipRect   ( pMyWindowData, NumHits );
                           for ( i=0; i<NumHits; i++ )
                           {
                              GpiDeleteSegment ( pMyWindowData->hps,
                                                 pMyWindowData->SegmentHits[i].Segment );
                           };
                           GpiSetClipRegion ( pMyWindowData->hps, hrgn, &hrgnold );
                           GpiDrawChain     ( pMyWindowData->hps );
                           GpiSetClipRegion ( pMyWindowData->hps, hrgnold, NULL );
                           GpiDestroyRegion ( pMyWindowData->hps, hrgn );
                           WinValidateRect  ( hWnd, (PRECTL) NULL, TRUE );
                        }
                        else
                        {
                           DosBeep ( 200, 200 );
                        };
                        return( (MRESULT) RECO_PROCESSED );
                        break;

                  /***********************************************************
                  * This event is mapped to the X-out gesture. We will use   *
                  * this to delete the entire segment under the hotspot.     *
                  ***********************************************************/
                  case  VE_DELETE:
                        NumHits = GpiCorrelateChain ( pMyWindowData->hps,
                                                      PICKSEL_VISIBLE,
                                                      &pMyWindowData->HotSpot,
                                                      1L,
                                                      1L,
                                                      &pMyWindowData->SegmentHits[0].Segment );
                        if ( NumHits )
                        {
                           hrgn = CreateClipRect   ( pMyWindowData, NumHits );
                           GpiDeleteSegment ( pMyWindowData->hps,
                                              pMyWindowData->SegmentHits[0].Segment );
                           GpiSetClipRegion ( pMyWindowData->hps, hrgn, &hrgnold );
                           GpiDrawChain     ( pMyWindowData->hps );
                           GpiSetClipRegion ( pMyWindowData->hps, hrgnold, NULL );
                           GpiDestroyRegion ( pMyWindowData->hps, hrgn );
                           WinValidateRect  ( hWnd, (PRECTL) NULL, TRUE );
                        }
                        else
                        {
                           DosBeep ( 200, 200 );
                        };
                        return( (MRESULT) RECO_PROCESSED );
                        break;

                  /***********************************************************
                  * By default the system maps a pigtail gesture to the      *
                  * virtual event for deleting a character. We will use this *
                  * to delete the stroke under the hot spot from the segment *
                  * under the hotspot.                                       *
                  ***********************************************************/
                  case  VE_DELETECHAR:
                        NumHits = GpiCorrelateChain ( pMyWindowData->hps,
                                                      PICKSEL_VISIBLE,
                                                      &pMyWindowData->HotSpot,
                                                      1L,
                                                      1L,
                                                      &pMyWindowData->SegmentHits[0].Segment );
                        if ( NumHits )
                           {
                           hrgn = CreateClipRect ( pMyWindowData, NumHits );
                           GpiSetDrawingMode     ( pMyWindowData->hps, DM_RETAIN );
                           GpiSetEditMode        ( pMyWindowData->hps, SEGEM_INSERT );
                           GpiOpenSegment        ( pMyWindowData->hps, pMyWindowData->SegmentHits[0].Segment );
                           GpiSetElementPointerAtLabel    ( pMyWindowData->hps,
                                                            pMyWindowData->SegmentHits[0].Element );
                           GpiDeleteElementsBetweenLabels ( pMyWindowData->hps,
                                                            LABEL_BEGIN_STROKE,
                                                            LABEL_END_STROKE );
                           GpiCloseSegment   ( pMyWindowData->hps );
                           GpiSetDrawingMode ( pMyWindowData->hps,
                                               DM_DRAWANDRETAIN );
                           GpiSetClipRegion  ( pMyWindowData->hps, hrgn, &hrgnold );
                           GpiDrawChain      ( pMyWindowData->hps );
                           GpiSetClipRegion  ( pMyWindowData->hps, hrgnold, NULL );
                           GpiDestroyRegion  ( pMyWindowData->hps, hrgn );
                           WinValidateRect   ( hWnd, (PRECTL) NULL, TRUE );
                           }
                        else
                           {
                           DosBeep ( 200, 200 );
                           };
                        return( (MRESULT) RECO_PROCESSED );
                        break;

                  /***********************************************************
                  * User has entered a letter gesture                        *
                  ***********************************************************/
                  case  VE_LETTERGESTURE:
                        switch ( pMyWindowData->RecoData.char_code )
                           {
                           /**************************************************
                           * Change the color to the currently selected      *
                           * color.                                          *
                           **************************************************/
                           case  'C':
                                 NumHits = GpiCorrelateChain ( pMyWindowData->hps,
                                                               PICKSEL_VISIBLE,
                                                               &pMyWindowData->HotSpot,
                                                               1L,
                                                               1L,
                                                               &pMyWindowData->SegmentHits[0].Segment );
                                 if ( NumHits )
                                    {
                                    ChangeColor ( pMyWindowData, pMyWindowData->CurrentColor );
                                    }
                                 else
                                    {
                                    DosBeep ( 200, 200 );
                                    };
                                 return( (MRESULT) RECO_PROCESSED );
                                 break;

                           /**************************************************
                           * Decrease the ink width                          *
                           **************************************************/
                           case  'D':
                                 NumHits = GpiCorrelateChain ( pMyWindowData->hps,
                                                               PICKSEL_VISIBLE,
                                                               &pMyWindowData->HotSpot,
                                                               1L,
                                                               1L,
                                                               &pMyWindowData->SegmentHits[0].Segment );
                                 if ( NumHits )
                                    {
                                    hrgn = CreateClipRect ( pMyWindowData, NumHits );
                                    GpiSetDrawingMode     ( pMyWindowData->hps, DM_RETAIN );
                                    GpiSetEditMode        ( pMyWindowData->hps, SEGEM_INSERT );
                                    GpiOpenSegment        ( pMyWindowData->hps, pMyWindowData->SegmentHits[0].Segment );
                                    GpiSetElementPointerAtLabel ( pMyWindowData->hps,
                                                                  LABEL_BEGIN_WIDTH );
                                    GpiOffsetElementPointer ( pMyWindowData->hps, 1L );
                                    GpiQueryElement         ( pMyWindowData->hps, 0L, sizeof(LWO), (PBYTE) &LineWidthOrder );
                                    if ( LineWidthOrder.width > 1 )
                                       {
                                       GpiSetElementPointer ( pMyWindowData->hps, 1L );
                                       GpiDeleteElementsBetweenLabels ( pMyWindowData->hps,
                                                                     LABEL_BEGIN_WIDTH,
                                                                     LABEL_END_WIDTH );
                                       GpiSetLineWidthGeom ( pMyWindowData->hps, LineWidthOrder.width/2 );
                                       };
                                    GpiCloseSegment   ( pMyWindowData->hps );
                                    GpiSetDrawingMode ( pMyWindowData->hps,
                                                        DM_DRAWANDRETAIN );
                                    GpiSetClipRegion  ( pMyWindowData->hps, hrgn, &hrgnold );
                                    GpiDrawChain      ( pMyWindowData->hps );
                                    GpiSetClipRegion  ( pMyWindowData->hps, hrgnold, NULL );
                                    GpiDestroyRegion  ( pMyWindowData->hps, hrgn );
                                    }
                                 else
                                    {
                                    DosBeep ( 200, 200 );
                                    };
                                 return( (MRESULT) RECO_PROCESSED );
                                 break;

                           /**************************************************
                           * Erase the presentation space, except for the    *
                           * background palegray box.                        *
                           **************************************************/
                           case  'E':
                                 GpiDeleteSegments ( pMyWindowData->hps, 2, pMyWindowData->SegmentName );
                                 pMyWindowData->SegmentName=1;
                                 GpiDrawChain ( pMyWindowData->hps );
                                 return( (MRESULT) RECO_PROCESSED );
                                 break;

                           /**************************************************
                           * Return to freehand drawing mode                 *
                           **************************************************/
                           case  'F':
                                 pMyWindowData->freehand = TRUE;
                                 StatusLine ( pMyWindowData, "Freehand Mode" );
                                 return( (MRESULT) RECO_PROCESSED );
                                 break;

                           /**************************************************
                           * Increase the ink width                          *
                           **************************************************/
                           case  'I':
                                 NumHits = GpiCorrelateChain ( pMyWindowData->hps,
                                                               PICKSEL_VISIBLE,
                                                               &pMyWindowData->HotSpot,
                                                               1L,
                                                               1L,
                                                               &pMyWindowData->SegmentHits[0].Segment );
                                 if ( NumHits )
                                    {
                                    GpiSetDrawingMode     ( pMyWindowData->hps, DM_RETAIN );
                                    GpiSetEditMode        ( pMyWindowData->hps, SEGEM_INSERT );
                                    GpiOpenSegment        ( pMyWindowData->hps, pMyWindowData->SegmentHits[0].Segment );
                                    GpiSetElementPointerAtLabel ( pMyWindowData->hps,
                                                                  LABEL_BEGIN_WIDTH );
                                    GpiOffsetElementPointer ( pMyWindowData->hps, 1L );
                                    GpiQueryElement     ( pMyWindowData->hps, 0L, sizeof(LWO), (PBYTE) &LineWidthOrder );
                                    GpiSetElementPointer ( pMyWindowData->hps, 1L );
                                    GpiDeleteElementsBetweenLabels ( pMyWindowData->hps,
                                                                     LABEL_BEGIN_WIDTH,
                                                                     LABEL_END_WIDTH );
                                    GpiSetLineWidthGeom ( pMyWindowData->hps, LineWidthOrder.width*2 );
                                    if ( LineWidthOrder.width == 1 )
                                       {
                                       while ( GpiSetElementPointerAtLabel ( pMyWindowData->hps,
                                                                             LABEL_BEGIN_BEGIN_PATH ) )
                                          {
                                          GpiBeginPath ( pMyWindowData->hps, 1L );
                                          GpiSetElementPointerAtLabel ( pMyWindowData->hps,
                                                                     LABEL_BEGIN_END_PATH );
                                          GpiEndPath ( pMyWindowData->hps );
                                          GpiStrokePath ( pMyWindowData->hps, 1L, 0L );
                                          };
                                       };
                                    GpiCloseSegment   ( pMyWindowData->hps );
                                    hrgn = CreateClipRect ( pMyWindowData, NumHits );
                                    GpiSetDrawingMode ( pMyWindowData->hps,
                                                        DM_DRAWANDRETAIN );
                                    GpiSetClipRegion  ( pMyWindowData->hps, hrgn, &hrgnold );
                                    GpiDrawChain      ( pMyWindowData->hps );
                                    GpiSetClipRegion  ( pMyWindowData->hps, hrgnold, NULL );
                                    GpiDestroyRegion  ( pMyWindowData->hps, hrgn );
                                    }
                                 else
                                    {
                                    DosBeep ( 200, 200 );
                                    };
                                 return( (MRESULT) RECO_PROCESSED );
                                 break;

                           default:
                                 break;
                           };
                        break;

                  default:
                        break;
                  }
                  return ( (MRESULT) RECO_MAP );
               break;

      /***********************************************************************
      * Make sure we resize the ink control when the window is resized       *
      ***********************************************************************/
      case  WM_SIZE:
               WinQueryWindowRect ( hWnd, &rcl );
               if ( pMyWindowData != NULL )
                  {
                  WinSetWindowPos ( pMyWindowData->InkColors,
                                    HWND_TOP,
                                    0,
                                    0,
                                    25,
                                    rcl.yTop,
                                    SWP_ACTIVATE | SWP_MOVE | SWP_SIZE | SWP_SHOW );
                  };
               break;

      /***********************************************************************
      * Repaint the window                                                   *
      ***********************************************************************/
      case  WM_PAINT:
               hpspaint = WinBeginPaint  ( hWnd, pMyWindowData->hps, &rcl );
               GpiDrawChain         ( hpspaint );
               WinUpdateWindow      ( pMyWindowData->InkColors );
               WinEndPaint          ( hpspaint );
               return ( (MRESULT) TRUE );
               break;

      /***********************************************************************
      * Clean up before we close the window                                  *
      ***********************************************************************/
      case  WM_CLOSE:
               GpiDestroyPS      ( pMyWindowData->hps );
               StatusLine        ( pMyWindowData, "" );
               WinDestroyWindow  ( WinQueryWindow ( hWnd, QW_PARENT ) );
               DosFreeMem        ( (PVOID) pMyWindowData );
               return ( (MRESULT) 0 );
               break;

      default:
               break;
      };
   return( (MRESULT) WinDefWindowProc ( hWnd, ulMsg, mp1, mp2 )  );
}

/*****************************************************************************
* Change the color of the selected segment.                                  *
*                                                                            *
* 1   Set the drawing mode to retain                                         *
* 2   Get the clipping region for the segment                                *
* 3   Set the edit mode to replace                                           *
* 4   Open the segment for editing                                           *
* 5   Position element pointer at the label for setting color                *
* 6   Increment the element pointer to the actual element that sets color    *
* 7   Replace element with new color                                         *
* 8   Close the segment                                                      *
* 9   Reset the drawing mode to draw and retain                              *
* 10  Set the clipping region and save old clipping region                   *
* 11  Draw the entire chain since other segments may cross the clipping      *
*     region                                                                 *
* 12  Reset the clipping region                                              *
* 13  Destroy the clipping regaion                                           *
* 14  Validate the window since the screen is now valid                      *
*                                                                            *
*****************************************************************************/
BOOL ChangeColor ( PMYWINDOWDATA pMyWindowData, LONG NewColor )
   {
   HRGN hrgn;
   HRGN hrgnold;

   GpiSetDrawingMode     ( pMyWindowData->hps, DM_RETAIN );
   hrgn = CreateClipRect ( pMyWindowData, 1 );
   GpiSetEditMode    ( pMyWindowData->hps, SEGEM_REPLACE );
   GpiOpenSegment    ( pMyWindowData->hps, pMyWindowData->SegmentHits[0].Segment );
   GpiSetElementPointerAtLabel ( pMyWindowData->hps, LABEL_BEGIN_COLOR );
   GpiOffsetElementPointer   ( pMyWindowData->hps, 1L );
   GpiSetColor       ( pMyWindowData->hps, NewColor );
   GpiCloseSegment   ( pMyWindowData->hps );
   GpiSetDrawingMode ( pMyWindowData->hps, DM_DRAWANDRETAIN );
   GpiSetClipRegion  ( pMyWindowData->hps, hrgn, &hrgnold );
   GpiDrawChain      ( pMyWindowData->hps );
   GpiSetClipRegion  ( pMyWindowData->hps, hrgnold, NULL );
   GpiDestroyRegion  ( pMyWindowData->hps, hrgn );
   WinValidateRect   ( pMyWindowData->MyWindow, (PRECTL) NULL, TRUE );
   return ( TRUE );
   }

/*****************************************************************************
* Calculate the clipping region for updating the window.                     *
*                                                                            *
* 1   Turn on the colletion of boundary data                                 *
* 2   Turn off the displaying of the updates while we collect the boundary   *
*     data                                                                   *
* 3   Draw each of the selected segments, with display off, to get the       *
*     boundary data for the segment                                          *
* 4   Add the boundary for this segment to the boundary data for all of the  *
*     segments being updated                                                 *
* 5   Increment the size of the bounding rectangle since the clipping region *
*     is an inclusive/exclusive rectangle                                    *
* 6   create and return the calculated clipping region                       *
*                                                                            *
*****************************************************************************/
HRGN CreateClipRect ( PMYWINDOWDATA  pMyWindowData, SHORT NumSegments )
{
   SHORT i;
   RECTL TmpBoundary1;
   RECTL TmpBoundary2;

   TmpBoundary2.xLeft   = 0;
   TmpBoundary2.xRight  = 0;
   TmpBoundary2.yTop    = 0;
   TmpBoundary2.yBottom = 0;

   GpiSetDrawControl    ( pMyWindowData->hps, DCTL_BOUNDARY, DCTL_ON );
   GpiSetDrawControl    ( pMyWindowData->hps, DCTL_DISPLAY, DCTL_OFF );
   for ( i=0; i<NumSegments; i++)
      {
      GpiDrawSegment       ( pMyWindowData->hps, pMyWindowData->SegmentHits[i].Segment );
      GpiQueryBoundaryData ( pMyWindowData->hps, &TmpBoundary1 );
      WinUnionRect         ( pMyWindowData->hab, &pMyWindowData->Boundary, &TmpBoundary1, &TmpBoundary2 );
      TmpBoundary2 = pMyWindowData->Boundary;
      };
   pMyWindowData->Boundary.xRight++;
   pMyWindowData->Boundary.yTop++;
   GpiSetDrawControl    ( pMyWindowData->hps, DCTL_DISPLAY, DCTL_ON );
   GpiSetDrawControl    ( pMyWindowData->hps, DCTL_BOUNDARY, DCTL_OFF );
   GpiConvert           ( pMyWindowData->hps, CVTC_MODEL, CVTC_DEVICE, 2L, (PPOINTL) &pMyWindowData->Boundary );
   pMyWindowData->Boundary.xRight++;
   pMyWindowData->Boundary.yTop++;
   return ( GpiCreateRegion ( pMyWindowData->hps, 1L, &pMyWindowData->Boundary ) );
}

/*****************************************************************************
* Create the ink color value set control on left side of the window          *
*****************************************************************************/
HWND CreateInkControls ( HWND hWnd )
{
   int      i;
   HWND     hValueSet;
   ULONG    ulValueSetStyles;
   VSCDATA  vscdata;
   RECTL    rcl;

   WinQueryWindowRect ( hWnd, &rcl );

   vscdata.cbSize        = sizeof (VSCDATA);
   vscdata.usRowCount    = 16;
   vscdata.usColumnCount = 1;

   ulValueSetStyles = VS_COLORINDEX |
                      VS_ITEMBORDER |
                      VS_BORDER;

   hValueSet = WinCreateWindow ( hWnd,
                                 WC_VALUESET,
                                 "",
                                 ulValueSetStyles,
                                 0, 0,
                                 25, rcl.yTop,
                                 hWnd,
                                 HWND_TOP,
                                 ID_VALUE_SET_INK,
                                 &vscdata,
                                 NULL );
   for (i=0; i<16; i++)
   {
      WinSendMsg ( hValueSet,
                   VM_SETITEM,
                   MPFROM2SHORT ( i+1, 1 ),
                   MPFROMLONG   ( InkColor[i] ) );
   };

   WinSendMsg ( hValueSet,
                VM_SELECTITEM,
                MPFROM2SHORT ( 1, 1 ),
                NULL );

   WinShowWindow ( hValueSet, TRUE );
   return ( hValueSet );
}

/*****************************************************************************
* Dialog procedure for displaying help                                       *
*****************************************************************************/
MRESULT APIENTRY InkEditHelp (  HWND     hWnd,
                                ULONG    ulMsg,
                                MPARAM   mp1,
                                MPARAM   mp2   )
{
   switch ( ulMsg )
      {
      case WM_COMMAND:
           switch ( SHORT1FROMMP(mp1) )
               {
               case  DID_CANCEL:
                     WinDismissDlg ( hWnd, FALSE );
                     return( (MRESULT) TRUE);
                     break;

               default:
                     break;
               };
           return((MRESULT) TRUE);
           break;

      default:
               break;
      };
   return( (MRESULT) WinDefDlgProc ( hWnd, ulMsg, mp1, mp2 ) );
}

