home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / cset21v6.zip / MMPM2TK / TK / CAPDLL / CCDLL.C < prev    next >
C/C++ Source or Header  |  1993-04-01  |  63KB  |  1,272 lines

  1. /******************************************************************************
  2.  * File Name   :  CCDLL.C
  3.  *
  4.  * Description :  This file contains the C source code required for the
  5.  *                caption DLL.
  6.  *
  7.  * Concepts    :  This caption DLL is part of MMPM/2 sample closed captioning
  8.  *                system.  Sample captioning system is composed of three
  9.  *                parts: Caption Creation Utility, Caption DLL (this DLL),
  10.  *                and Caption Sample Application. The Caption Creation
  11.  *                Utility creates a "captioned" file.  This is a text file
  12.  *                with timing information relating to it's associated audio
  13.  *                file. The The Caption DLL (this DLL) provides 3 API's that
  14.  *                drive the display and management of a "caption window" in a
  15.  *                PM application.  The Caption Sample Application illustrates
  16.  *                how an application uses the 3 API's provided by the caption
  17.  *                DLL (this DLL) to take advantage of it's services.
  18.  *
  19.  *                This sample captioning system is provided to illustrate one
  20.  *                of the many ways that captioning may be implemented using
  21.  *                MMPM/2. As with all MMPM/2 samples, this may be used as it
  22.  *                is provided, or it may be modified in any way you prefer.
  23.  *                Please refer to the Application Programmers Reference for
  24.  *                more information on this sample captioning system.
  25.  *
  26.  * MMPM/2 API's:  List of all MMPM/2 API's that are used in
  27.  *                this module
  28.  *
  29.  *                mciSendString
  30.  *                   MCI_SET_POSITION_ADVISE
  31.  *
  32.  * Required
  33.  *    Files    :  ccdll.c           Source Code.
  34.  *                captions.h        Include file.
  35.  *                makefile          Make file.
  36.  *                cap.def           Linker definition file.
  37.  *
  38.  * Copyright (C) IBM 1993
  39.  ******************************************************************************/
  40.  
  41. #define INCL_GPILCIDS
  42. #define INCL_GPIPRIMITIVES
  43. #define INCL_GPIBITMAPS
  44. #define INCL_WIN
  45. #define INCL_DOSPROCESS
  46. #define INCL_OS2MM                 /* required for MCI and MMIO headers   */
  47. #define INCL_WINWINDOWMGR
  48. #define INCL_WINMESSAGEMGR
  49. #define INCL_WINDIALOGS
  50. #define INCL_DOSMODULEMGR
  51.  
  52. #include <stdlib.h>                  /*---------------------------------------*/
  53. #include <string.h>                  /*   Include standard c-library files    */
  54. #include <stdio.h>                   /*---------------------------------------*/
  55.  
  56. #include <os2.h>                     /*-----  OS/2 system header files -------*/
  57.  
  58. #include <os2me.h>                   /*-- Headers for multimedia extensions --*/
  59.  
  60. #include "captions.h"                /*--- General closed-captioning header --*/
  61.  
  62.  
  63. /*------------------------- Local definitions --------------------------------*/
  64.  
  65. #define CC_TIMER_ID           1110
  66. #define CC_TIMER_FREQ         100
  67. #define CC_DEFAULT_TEXT_LINES 3      /* Default window height in text lines   */
  68. #define CC_DEFAULT_CHARS_WIDE 35     /* Default window width in characters    */
  69.  
  70. #define MAX_TEXT_LINES        500
  71.  
  72. #define CHAR_NULL             0   /*------------------------------------------*/
  73. #define CHAR_RETURN           13  /* Control characters present in ASCII files*/
  74. #define CHAR_EOF              26  /*------------------------------------------*/
  75.  
  76. #define CC_FILE               "CC_FILE"
  77.  
  78.  
  79. /*------------ Values for ulFlags in CCINSTANCE strucuture -------------------*/
  80.  
  81. #define CC_TIMER_RUNNING   0x02   /* Set if the timer is running              */
  82. #define CC_CAPTIONS_ACTIVE 0x04   /* Set when we are actually doing captioning*/
  83. #define CC_DRAG_MODE       0x08   /* Set when the cap. win. is being dragged  */
  84. #define CC_USER_POSITION   0x10   /* Set when user has set win pos            */
  85.  
  86.  
  87. /*------- This is the instance data for the closed-caption window ------------*/
  88.  
  89. typedef _Packed struct _CCINSTANCE
  90. {
  91.    HAB      hab;               /* Anchor block for this window                */
  92.    HWND     hwndCaption;       /* Handle of the caption window                */
  93.    HWND     hwndParent;        /* Window in which caption window is displayed */
  94.    HWND     hwndPosition;      /* Send position messages here                 */
  95.    PFNWP    pfnOldWinProc;     /* Old window procedure of the parent window   */
  96.    RECTL    rclWin;            /* Latest dimensions of caption window         */
  97.    RECTL    rclText;           /* Size of the backup bitmap for the cap. win. */
  98.    HPS      hpsText;           /* Pres. space handle for the backup bitmap    */
  99.    HBITMAP  hbmText;           /* Bitmap handle for the backup bitmap         */
  100.    HDC      hdcText;           /* Device context for the backup bitmap        */
  101.    PVOID    pvLinedataTable;   /* ^ to table of strings and times to display  */
  102.    PVOID    pvText;            /* Pointer to the caption file text            */
  103.    LONG     lScrollOffset;     /* Offset if scrolling new line into window    */
  104.    LONG     lScrollInc;        /* Scroll step size                            */
  105.    LONG     lLineSpacing;      /* Y-pixels for each line                      */
  106.    LONG     lLeftMargin;       /* X-pixel start for each line                 */
  107.    LONG     lBottomLineY;      /* Y-coordinate of bottom-most line in cap. win*/
  108.    LONG     lWindowColor;      /* Background color of window                  */
  109.    LONG     lTextColor;        /* Color of text in window                     */
  110.    LONG     lWindowXcoord;     /* Cap. win. position if CC_USER_POSITION set  */
  111.    LONG     lWindowYcoord;     /* Cap. win. position if CC_USER_POSITION set  */
  112.    USHORT   usWindowLines;     /* Number of lines to display in window        */
  113.    USHORT   usWindowChars;     /* Width of window, in text characters         */
  114.    USHORT   usNextlineReq;     /* Number of queued line requests              */
  115.    USHORT   usDisplayLine;     /* Index of BOTTOM-most line in capaption win  */
  116.    USHORT   usLineCount;       /* Number of lines in caption file             */
  117.    ULONG    ulTimePrev;        /* Time of previous position change message    */
  118.    MPARAM   mpMouseCoords;     /* Mouse coords at start of drag               */
  119.    HPOINTER hptrMove;          /* Handle of the SPTR_MOVE pointer             */
  120.    ULONG    ulFlags;           /* Flags for TRUE/FALSE data                   */
  121. }
  122. CCINSTANCE;
  123.  
  124. typedef CCINSTANCE FAR *PCCINSTANCE;
  125.                                   /*------------- Line data structure --------*/
  126.                                   /*                                          */
  127. typedef _Packed struct _LINEDATA  /* There is one for each caption line       */
  128. {                                 /*                                          */
  129.    PSZ    szText;                 /* <-Pointer to a NULL-terminated text str. */
  130.    LONG   lTextLen;               /* <-Number of characters in the string     */
  131.    ULONG  ulTime;                 /* <-Time (MM units) to display the string  */
  132. }                                 /*                                          */
  133. LINEDATA;                         /* An array of structures is maintained     */
  134.                                   /* in a table pointed to by pvLinedataTable.*/
  135. typedef LINEDATA FAR *PLINEDATA;  /*                                          */
  136.                                   /*------------------------------------------*/
  137.  
  138. /*----------------- Function prototype statements ----------------------------*/
  139.  
  140. MRESULT EXPENTRY CaptionWindowProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);
  141. static  APIRET   StartCaptioning( PCCINSTANCE pid, CHAR szFilename[],
  142.                                      HWND hwndPosition, CHAR szDeviceName[] );
  143. static  VOID     FormatTextBitmap( PCCINSTANCE pid, USHORT usLastLine );
  144. static  APIRET   CreateBitmap( PCCINSTANCE pid );
  145. static  APIRET   LoadCaptionFile( PCCINSTANCE pid, CHAR szCaptionFile[] );
  146. static  VOID     StopCaptioning( PCCINSTANCE pid );
  147. static  void     PositionCaptionWindow( PCCINSTANCE pid );
  148.  
  149. #define CC_WINCLASS     "CapClass"
  150.  
  151.  
  152. /*------------------------------- Global data area ---------------------------*/
  153. HMODULE hmodule;      /* The module handle of the captioning DLL              */
  154.  
  155. /******************************************************************************
  156.  * Name         : ccInitialize
  157.  *
  158.  * Description  : This API creates a captioning window and returns the handle
  159.  *                to the application. Call this function once to associate a
  160.  *                captioning window with a user window.
  161.  *
  162.  * Parameters   : hwndParent - The handle of the user window in where the
  163.  *                             captioning window will appear.
  164.  *
  165.  * Return       : ULONG      - The handle of the captioning window.  If the
  166.  *                             function was unsuccessful, this will be NULL.
  167.  *
  168.  ******************************************************************************/
  169. ULONG APIENTRY ccInitialize ( HWND hwndParent )
  170. {
  171.    HAB     hab;                           /* Parent window's anchor block     */
  172.    PFNWP   pfnCaptionWindowProc;          /* Pointer to DLL entry             */
  173.    HWND    hwndCaption;                   /* Handle of the caption window     */
  174.  
  175.  
  176.    if ( hwndParent == (HWND)NULL )
  177.       return( CCERR_INVALID_WINDOW_HANDLE );
  178.  
  179.    /*---------------- Create the captioning window ---------------------------*/
  180.    /*                                                                         */
  181.    /* The captioning window is created as a child of the specified user       */
  182.    /* window.  It does not become visible until the user issues a CC_START    */
  183.    /* message.  Note that the captioning window may be overpainted by the     */
  184.    /* user's parent window unless the user's window has the WS_CLIPCHILDREN   */
  185.    /* style set.                                                              */
  186.    /*-------------------------------------------------------------------------*/
  187.  
  188.    hab = WinQueryAnchorBlock ( (HWND) hwndParent );
  189.  
  190.    WinRegisterClass ( hab, CC_WINCLASS, CaptionWindowProc,
  191.                                         CS_SYNCPAINT | CS_SIZEREDRAW, 4 );
  192.  
  193.    hwndCaption = WinCreateWindow  ( (HWND) hwndParent, CC_WINCLASS, NULL,
  194.                                      0, 0, 0, 1, 1, (HWND) hwndParent,
  195.                                      HWND_TOP, CC_WINDOW_ID, NULL, NULL );
  196.  
  197.    return (ULONG) hwndCaption;
  198. }
  199.  
  200. /******************************************************************************
  201.  * Name         : ccSendCommand
  202.  *
  203.  * Description  : This API is used to control the captioning window once it
  204.  *                has been created.
  205.  *
  206.  * Parameters   : usMsg - Captioning command.
  207.  *                mp1   - Window handle of caption window.
  208.  *                mp2   - Varies depending on command.  See commands below.
  209.  *
  210.  * Return       : ULONG - Captioning DLL's response to the message.
  211.  *
  212.  ******************************************************************************/
  213. ULONG APIENTRY ccSendCommand ( USHORT usMsg, MPARAM mp1, MPARAM mp2 )
  214. {
  215.    USHORT  msg;
  216.  
  217.    switch ( usMsg )
  218.    {                        /*---------------------- CC_START ----------------*/
  219.       case CC_START:        /*  mp1 - Reserved.  Must be set to zero.         */
  220.          msg = UMCC_START;  /*  mp2 - Pointer to a CC_START_PARMS structure   */
  221.          break;             /*------------------------------------------------*/
  222.  
  223.                             /*---------------------- CC_STOP -----------------*/
  224.       case CC_STOP:         /*  mp1 - Reserved.  Must be set to zero.         */
  225.          msg = UMCC_STOP;   /*  mp2 - Reserved.  Must be set to zero.         */
  226.          break;             /*------------------------------------------------*/
  227.  
  228.                             /*----------------------- CC_SET -----------------*/
  229.       case CC_SET:          /*  mp1 - Reserved.  Must be set to zero.         */
  230.          msg = UMCC_SET;    /*  mp2 - Pointer to a CC_SET_PARMS structure.    */
  231.          break;             /*------------------------------------------------*/
  232.  
  233.                             /*------------- CC_STATUS ------------------------*/
  234.       case CC_STATUS:       /*  mp1 - Reserved.  Must be set to zero.         */
  235.          msg = UMCC_STATUS; /*  mp2 - Pointer to a CC_STATUS_PARMS structure. */
  236.          break;             /*------------------------------------------------*/
  237.  
  238.       default:
  239.          msg = UMCC_ERROR; /*  mp2 - Pointer to a CC_STATUS_PARMS structure. */
  240.          break;
  241.    }
  242.  
  243.    /*
  244.     * After assigning the appropriate value to message, send it over to the
  245.     * window procedure to be processed.  Obtain the handle of the caption
  246.     * window by extracting it from message parameter 1, where it was passed in.
  247.     */
  248.    return (ULONG) WinSendMsg ( HWNDFROMMP(mp1), msg, mp1, mp2 );
  249. }
  250.  
  251. /******************************************************************************
  252.  * Name         : ccTerminate
  253.  *
  254.  * Description  : This API closes the captioning system and releases any
  255.  *                resources.  Call this API once at the end of the appliaction.
  256.  *
  257.  * Parameters   : hwndCaption - window handle of caption window.
  258.  *
  259.  * Return       : None.
  260.  *
  261.  ******************************************************************************/
  262. VOID APIENTRY ccTerminate (HWND hwndCaption)
  263. {
  264.    WinDestroyWindow ( hwndCaption );
  265.    DosFreeModule    ( hmodule );
  266. }
  267.  
  268. /******************************************************************************
  269.  * Name         : CaptionWindowProc
  270.  *
  271.  * Description  : This function controls the caption window.  It will handle
  272.  *                received messages from the user application.
  273.  *
  274.  * Concepts     : - How to handle notification messages.
  275.  *
  276.  * MMPM/2 API's :
  277.  *
  278.  * Parameters   : hwnd - Handle for the text window
  279.  *                msg  - Message received by the text dialog box.
  280.  *                mp1  - Parameter 1 for the just received message.
  281.  *                mp2  - Parameter 2 for the just received message.
  282.  *
  283.  * Return       :
  284.  *
  285.  ******************************************************************************/
  286. MRESULT EXPENTRY CaptionWindowProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  287. {
  288.    PCCINSTANCE      pid;         /* Pointer to the instance data for this win.*/
  289.    PLINEDATA        pld;         /* Pointer used to scan Line Data Table      */
  290.    PCREATESTRUCT    pcs;         /* Pointer to window creation structure      */
  291.    PCC_START_PARMS  psp;         /* Pointer to CC_START_PARMS                 */
  292.    PCC_SET_PARMS    pSetParms;   /* Pointer to CC_SET_PARMS structure         */
  293.    PCC_STATUS_PARMS pStatusParms;/* Pointer to CC_STATUS_PARMS structure      */
  294.  
  295.    HPS              hpsPaint;    /*-------------------------------------------*/
  296.    RECTL            rclPaint;    /* Variables used in painting caption win.   */
  297.    POINTL           aptlPaint[4];/*-------------------------------------------*/
  298.  
  299.    USHORT           usOutputLine;/*--------  These variables are used  -------*/
  300.    ULONG            ulTime;      /*          when calculating which           */
  301.    USHORT           usTestLine;  /*--------  caption line to show next -------*/
  302.  
  303.    ULONG            ulMemreq;    /* Bytes of memory needed for instance data. */
  304.    RECTL            rclParent;   /*     Updated dimensions of parent window   */
  305.    LONG             lXcoord, lYcoord;/* Updated origin of captioning window   */
  306.    APIRET           apiret;      /*  Value recturned with UMCC_START message  */
  307.    SWP              swp;
  308.  
  309.                                                       /*----------------------*/
  310.    if ( msg != WM_CREATE )                            /* Get a pointer to the */
  311.       pid = (PCCINSTANCE) WinQueryWindowPtr(hwnd, 0); /* window instance data */
  312.                                                       /*----------------------*/
  313.    switch ( msg )
  314.    {
  315.       case WM_CREATE:
  316.          DosAllocMem ( (PPVOID) &pid, sizeof(CCINSTANCE), fALLOC );
  317.          WinSetWindowPtr ( hwnd, 0, pid );
  318.          pcs = (PCREATESTRUCT) PVOIDFROMMP(mp2);
  319.  
  320.          memset ( pid, 0, (size_t) sizeof(CCINSTANCE) );  /*------------------*/
  321.          pid->hab           = WinQueryAnchorBlock( hwnd );/*                  */
  322.          pid->hwndCaption   = hwnd;                       /*                  */
  323.          pid->hwndParent    = pcs->hwndParent;            /*                  */
  324.          pid->usWindowLines = CC_DEFAULT_TEXT_LINES;      /* During WM_CREATE */
  325.          pid->usWindowChars = CC_DEFAULT_CHARS_WIDE;      /* processing, we   */
  326.          pid->lWindowColor  = CLR_WHITE;                  /* allocate and     */
  327.          pid->lTextColor    = CLR_DARKBLUE;               /* initialize the   */
  328.          pid->lLeftMargin   = 10;                         /* window instance  */
  329.          pid->lScrollInc    = 3;                          /* data.            */
  330.          pid->rclWin.xRight = pcs->cx;                    /*                  */
  331.          pid->rclWin.yTop   = pcs->cy;                    /*------------------*/
  332.          pid->hptrMove      =
  333.                      WinQuerySysPointer( HWND_DESKTOP, SPTR_MOVE, FALSE );
  334.          break;
  335.  
  336.  
  337.       case WM_SIZE:                               /*--------------------------*/
  338.          WinQueryWindowRect( hwnd, &pid->rclWin );/* Update the instance data */
  339.          break;                                   /* every time the window is */
  340.                                                   /* resized.                 */
  341.                                                   /*--------------------------*/
  342.  
  343.       /*---------------- Dragging the caption window -------------------------*/
  344.       /*                                                                      */
  345.       /*  The next three messages (WM_BUTTON1DOWN, WM_MOUSEMOVE, WM_BUTTON1UP)*/
  346.       /*  are used for dragging the caption window.                           */
  347.       /*                                                                      */
  348.       /*  (1) When we receive a WM_BUTTON1DOWN message, we store the          */
  349.       /*      coordinates of the pointer (relative to the Caption Window) in  */
  350.       /*      the window instance data.                                       */
  351.       /*                                                                      */
  352.       /*  (2) When we receive a WM_MOUSEMOVE message, we check to see if the  */
  353.       /*      pointer is still at the same relative position in the Caption   */
  354.       /*      Window, and...                                                  */
  355.       /*                                                                      */
  356.       /*  (3) If the pointer has moved relative to the window, we move the    */
  357.       /*      window enough to restore the original pointer position.         */
  358.       /*                                                                      */
  359.       /*  (4) When the user releases the left mouse button, we end drag mode  */
  360.       /*      and...                                                          */
  361.       /*                                                                      */
  362.       /*  (5) Save the new window position for future captioning requests     */
  363.       /*                                                                      */
  364.       /*----------------------------------------------------------------------*/
  365.  
  366.       case WM_BUTTON1DOWN:
  367.          WinSetCapture ( HWND_DESKTOP, hwnd );
  368.          WinSetPointer ( HWND_DESKTOP, pid->hptrMove );
  369.          pid->ulFlags |= CC_DRAG_MODE;
  370.          pid->mpMouseCoords = mp1;               /* (1) Save pointer coords   */
  371.          return 0;
  372.  
  373.  
  374.       case WM_MOUSEMOVE:
  375.          if ( pid->ulFlags & CC_DRAG_MODE )
  376.          {
  377.             if ( mp1 != pid->mpMouseCoords )     /* (2) Check for alignment   */
  378.             {
  379.                WinQueryWindowPos ( hwnd, &swp );
  380.                swp.x += ( SHORT1FROMMP(mp1) - SHORT1FROMMP(pid->mpMouseCoords));
  381.                swp.y += ( SHORT2FROMMP(mp1) - SHORT2FROMMP(pid->mpMouseCoords));
  382.                WinSetWindowPos(hwnd, 0, swp.x, swp.y, 0, 0, SWP_MOVE );/*-(3)-*/
  383.             }
  384.             WinSetPointer ( HWND_DESKTOP, pid->hptrMove );
  385.             return 0;
  386.          }
  387.          break;
  388.  
  389.  
  390.       case WM_BUTTON1UP:
  391.          if ( pid->ulFlags & CC_DRAG_MODE )
  392.          {
  393.             pid->ulFlags &= ~CC_DRAG_MODE;        /* (4) End drag mode        */
  394.             WinSetCapture ( HWND_DESKTOP, 0 );
  395.  
  396.             WinQueryWindowPos ( hwnd, &swp );     /* (5) Store position for   */
  397.             pid->lWindowXcoord = swp.x;           /*     future requests.     */
  398.             pid->lWindowYcoord = swp.y;
  399.             pid->ulFlags |= CC_USER_POSITION;
  400.          }
  401.          break;
  402.  
  403.  
  404. /*----------------------------------------------------------------------------*/
  405. /*                                                                            */
  406. /*  When a WM_PAINT message is received, update the caption window from the   */
  407. /*  text bitmap.  In calculating the SOURCE blit rectangle, it is necessary to*/
  408. /*  take out account:                                                         */
  409. /*                                                                            */
  410. /*    (a) The window may not be the same size as the bitmap ( horiz & vert )  */
  411. /*    (b) The window may be scrolled with respect to the bitmap ( vert only ) */
  412. /*                                                                            */
  413. /*  Note:  In this iteration of the program, the window and the backup bitmap */
  414. /*         always copy pixel-for-pixel.  However, in future versions this     */
  415. /*         might be so.  Therefore I have put in scaling logic to accommodate */
  416. /*         any future version which might need it.                            */
  417. /*                                                                            */
  418. /*----------------------------------------------------------------------------*/
  419.  
  420.       case WM_PAINT:
  421.          hpsPaint = WinBeginPaint ( hwnd, 0, &rclPaint );
  422.  
  423.          aptlPaint[0].x = rclPaint.xLeft;  /*---------------------------------*/
  424.          aptlPaint[0].y = rclPaint.yBottom;/* The TARGET blit rect. is always */
  425.          aptlPaint[1].x = rclPaint.xRight; /* the same as the paint rectangle */
  426.          aptlPaint[1].y = rclPaint.yTop;   /*---------------------------------*/
  427.  
  428.          aptlPaint[2].x =
  429.              rclPaint.xLeft   * pid->rclText.xRight / pid->rclWin.xRight;
  430.          aptlPaint[2].y =
  431.              rclPaint.yBottom * pid->rclText.yTop   / pid->rclWin.yTop
  432.                                                     + pid->lScrollOffset;
  433.          aptlPaint[3].x =
  434.              rclPaint.xRight  * pid->rclText.xRight / pid->rclWin.xRight;
  435.          aptlPaint[3].y =
  436.              rclPaint.yTop    * pid->rclText.yTop   / pid->rclWin.yTop
  437.                                                     + pid->lScrollOffset;
  438.  
  439.          GpiSetColor    ( hpsPaint, pid->lWindowColor );/* Normal Window color*/
  440.          GpiSetBackColor( hpsPaint, pid->lTextColor   );/* Normal Text color  */
  441.  
  442.          GpiBitBlt      ( hpsPaint, pid->hpsText, 4, aptlPaint,
  443.                                                      ROP_SRCCOPY, BBO_IGNORE );
  444.          WinEndPaint    ( hpsPaint );
  445.  
  446.          return (MRESULT) 0;
  447.  
  448.  
  449. /*----------------------------------------------------------------------------*/
  450. /*    We get a WM_TIMER message every time it is necessary to scroll the      */
  451. /* captions.  The scroll increment is specified by lScrollInc in the window   */
  452. /* instance data.  The caption window is repainted as part of the (1)         */
  453. /* WinScrollWindow operation, because of its CS_SYNCPAINT class style.        */
  454. /*    After each scroll operation, we (2) check to see if we have scrolled    */
  455. /* one entire text line.  If this is so, stop the timer and (3) check         */
  456. /* usNextlineReq to see if there are any more lines queued up to be scrolled. */
  457. /*----------------------------------------------------------------------------*/
  458.  
  459.       case WM_TIMER:
  460.          pid->lScrollOffset -= pid->lScrollInc;
  461.          WinScrollWindow( hwnd, 0, pid->lScrollInc, NULL, /* (1) Scroll window*/
  462.                           NULL, 0, NULL, SW_INVALIDATERGN );
  463.          if ( ! pid->lScrollOffset )                    /* (2) Text line done?*/
  464.          {
  465.             WinStopTimer ( pid->hab, hwnd, CC_TIMER_ID );
  466.             pid->ulFlags &= ~CC_TIMER_RUNNING;
  467.             if ( pid->usNextlineReq ) pid->usNextlineReq--;
  468.             if ( pid->usNextlineReq )                     /* (3) Check for    */
  469.                WinPostMsg ( hwnd, UMCC_NEXTLINE, 0, 0 );  /*     queued lines.*/
  470.          }
  471.          break;
  472.  
  473.  
  474. /*----------------------------------------------------------------------------*/
  475. /*                                                                            */
  476. /* There are two cases in processing an MM_MCIPOSITIONCHANGE message:         */
  477. /*                                                                            */
  478. /*    (1) The time in the message is LATER than the previous message (saved   */
  479. /*        in the window instance data ).  In this case, check to see if it's  */
  480. /*        time to scroll another line into the caption window.                */
  481. /*                                                                            */
  482. /*    (2) The time in the message is EARLIER than the previous message.  This */
  483. /*        means that the user has rewound or backed up the audio file.  Stop  */
  484. /*        any current scrolling operation; reposition the usDisplayLine       */
  485. /*        pointer, and show the repositioned captions.                        */
  486. /*                                                                            */
  487. /* In any case, check to see if the user has requested us to forward the      */
  488. /* position messages to his window.                                           */
  489. /*                                                                            */
  490. /*----------------------------------------------------------------------------*/
  491.  
  492.       case MM_MCIPOSITIONCHANGE:
  493.          if ( pid->ulFlags & CC_CAPTIONS_ACTIVE )
  494.          {
  495.             ulTime = (ULONG) LONGFROMMP(mp2);
  496.             if ( ulTime >= pid->ulTimePrev )       /********* CASE 1 **********/
  497.             {
  498.                usTestLine = pid->usDisplayLine + pid->usNextlineReq + 1;
  499.                if ( usTestLine >= (pid->usLineCount + 5) ) break;
  500.                pld = (PLINEDATA) pid->pvLinedataTable + usTestLine;
  501.                while ((usTestLine<pid->usLineCount) && (pld->ulTime<=ulTime))
  502.                {
  503.                   pid->usNextlineReq++;
  504.                   if ( pid->usNextlineReq == 1 )
  505.                      WinPostMsg(hwnd,UMCC_NEXTLINE,MPFROMLONG((LONG)ulTime), 0);
  506.                   pld++;
  507.                   usTestLine++;
  508.                }
  509.             }
  510.             else
  511.             {                                      /********* CASE 2 **********/
  512.                if ( pid->ulFlags & CC_TIMER_RUNNING )
  513.                {
  514.                   WinStopTimer ( pid->hab, hwnd, CC_TIMER_ID );
  515.                   pid->ulFlags &= ~CC_TIMER_RUNNING;
  516.                }
  517.                pld = (PLINEDATA) pid->pvLinedataTable;
  518.                usTestLine = 0;
  519.                while ((usTestLine<pid->usLineCount) && (pld->ulTime<=ulTime))
  520.                {
  521.                   usTestLine++;
  522.                   pld++;
  523.                }
  524.                FormatTextBitmap ( pid, usTestLine );
  525.                pid->usNextlineReq = 0;
  526.                pid->usDisplayLine = usTestLine;
  527.                WinInvalidateRect ( hwnd, 0, FALSE );
  528.             }
  529.  
  530.             pid->ulTimePrev = ulTime;              /* Forward msg if requested*/
  531.             if ( pid->hwndPosition != 0 )
  532.                return WinSendMsg ( pid->hwndPosition, msg, mp1, mp2 );
  533.          }
  534.          return 0;
  535.  
  536.  
  537.       /*-------------- User request to start captioning ----------------------*/
  538.  
  539.       case UMCC_START:
  540.          psp = (PCC_START_PARMS) PVOIDFROMMP(mp2);
  541.          apiret = StartCaptioning( pid, (PCHAR)psp->pszCaptionFile,
  542.                             (HWND)psp->hwndOwner, (PCHAR)psp->pszDeviceName   );
  543.          return (MRESULT) apiret;
  544.  
  545.  
  546.       /*--------------- User request to stop captioning ----------------------*/
  547.  
  548.       case UMCC_STOP:
  549.          StopCaptioning ( pid );
  550.          return 0;
  551.  
  552.       /*--------------- When we receive an invalid status request.------------*/
  553.  
  554.       case UMCC_ERROR:
  555.          return (MRESULT) CCERR_INVALID_COMMAND;
  556.  
  557.       /*----------------------------------------------------------------------*/
  558.       /* When we receive a status request, move the requested information     */
  559.       /* from the window instance data to the ulReturn field of the status    */
  560.       /* parameters.                                                          */
  561.       /*----------------------------------------------------------------------*/
  562.  
  563.       case UMCC_STATUS:
  564.          pStatusParms = (PCC_STATUS_PARMS) PVOIDFROMMP(mp2);
  565.  
  566.          switch ( pStatusParms->ulItem )
  567.          {
  568.             case CC_STATUS_TEXT_COLUMNS:
  569.                pStatusParms->ulReturn = (ULONG) pid->usWindowChars;
  570.                break;
  571.  
  572.             case CC_STATUS_TEXT_ROWS:
  573.                pStatusParms->ulReturn = (ULONG) pid->usWindowLines;
  574.                break;
  575.  
  576.             case CC_STATUS_TEXT_COLOR:
  577.                pStatusParms->ulReturn = (ULONG) pid->lTextColor;
  578.                break;
  579.  
  580.             case CC_STATUS_BACKGROUND_COLOR:
  581.                pStatusParms->ulReturn = (ULONG) pid->lWindowColor;
  582.                break;
  583.  
  584.             case CC_STATUS_X_POSITION:
  585.                pStatusParms->ulReturn = (ULONG) pid->lWindowXcoord;
  586.                break;
  587.  
  588.             case CC_STATUS_Y_POSITION:
  589.                pStatusParms->ulReturn = (ULONG) pid->lWindowYcoord;
  590.  
  591.             default:
  592.               pStatusParms->ulReturn = (ULONG) CCERR_INVALID_STATUS_REQUEST;
  593.          }
  594.          return 0;
  595.  
  596.  
  597.       /*--------------- Setting captioning parameters ------------------------*/
  598.       /*                                                                      */
  599.       /*  There are two phases involved in setting captioning parameters:     */
  600.       /*                                                                      */
  601.       /*  (1) Move the data from the UMCC_SET message to the appropriate      */
  602.       /*      area(s) in the instance data for the captioning window.         */
  603.       /*                                                                      */
  604.       /*  (2) If captioning is currently in progress, update the captioning   */
  605.       /*      window to reflect these changes...                              */
  606.       /*                                                                      */
  607.       /*      (2a) When changing window colors, force a repaint of the window.*/
  608.       /*                                                                      */
  609.       /*      (2b) When changing the number of columns or rows, we have to    */
  610.       /*           destroy the old backup text bitmap and create a new one.   */
  611.       /*           The captioning window must be hidden during this operation */
  612.       /*           so it doesn't get any WM_PAINT message while the backup    */
  613.       /*           bitmap is in limbo.  For the same reason, scrolling        */
  614.       /*           operations and new line requests must be shut off.  Once   */
  615.       /*           the new bitmap is made, reposition and show the resized    */
  616.       /*           captioning window.                                         */
  617.       /*                                                                      */
  618.       /*      (2c) When changing the window position, move the captioning     */
  619.       /*           window                                                     */
  620.       /*                                                                      */
  621.       /*----------------------------------------------------------------------*/
  622.  
  623.       case UMCC_SET:
  624.          pSetParms = (PCC_SET_PARMS) PVOIDFROMMP(mp2);
  625.          pid->usWindowChars = (USHORT) pSetParms->ulColumns;
  626.          pid->usWindowLines = (USHORT) pSetParms->ulRows;
  627.          pid->lTextColor = (LONG) pSetParms->ulTextColor;
  628.          pid->lWindowColor = (LONG) pSetParms->ulBackgroundColor;
  629.          pid->ulFlags |= CC_USER_POSITION;
  630.          pid->lWindowXcoord = (LONG) pSetParms->ulXposition;
  631.          pid->ulFlags |= CC_USER_POSITION;
  632.          pid->lWindowYcoord = (LONG) pSetParms->ulYposition;
  633.  
  634.          if (pid->ulFlags & CC_CAPTIONS_ACTIVE) /*(2)Update caption window, if*/
  635.          {
  636.             WinInvalidateRect ( hwnd, NULL, FALSE );
  637.  
  638.             WinSetWindowPos ( hwnd, 0, 0, 0, 0, 0, SWP_HIDE );
  639.             if ( pid->ulFlags & CC_TIMER_RUNNING )
  640.             {
  641.                 WinStopTimer ( pid->hab, hwnd, CC_TIMER_ID );
  642.                 pid->ulFlags &= ~CC_TIMER_RUNNING;
  643.             }
  644.             pid->lScrollOffset = 0;
  645.             pid->usNextlineReq = 0;
  646.             apiret = CreateBitmap ( pid );
  647.  
  648.             if (apiret)
  649.               return (MRESULT) apiret;
  650.  
  651.             FormatTextBitmap ( pid, pid->usDisplayLine );
  652.             PositionCaptionWindow ( pid );
  653.             WinSetWindowPos ( hwnd, 0, 0, 0, 0, 0, SWP_SHOW );
  654.  
  655.             PositionCaptionWindow ( pid );
  656.          }
  657.  
  658.          return 0;
  659.  
  660.       /*----------------------------------------------------------------------*/
  661.       /*  We receive this message when it is time to scroll to the next line. */
  662.       /*  First make sure that (1) the audio file has not been backspaced     */
  663.       /*  since the messagewas issued.  If the file has not been backspaced,  */
  664.       /*  go ahead and (2) start the scroll operation.                        */
  665.       /*----------------------------------------------------------------------*/
  666.  
  667.       case UMCC_NEXTLINE:
  668.          if ( pid->ulFlags & CC_CAPTIONS_ACTIVE )
  669.          {
  670.             ulTime = (ULONG) LONGFROMMP(mp1);
  671.             if (ulTime <= pid->ulTimePrev) /*(1)Check to see if audio backspcd*/
  672.             {
  673.                pid->usDisplayLine++;
  674.                if ( pid->usDisplayLine < pid->usLineCount )
  675.                {
  676.                   FormatTextBitmap(pid, pid->usDisplayLine);/*(2)Initiate scrl*/
  677.                   pid->lScrollOffset = pid->lLineSpacing;
  678.                   WinStartTimer ( pid->hab, hwnd, CC_TIMER_ID, CC_TIMER_FREQ );
  679.                   pid->ulFlags |= CC_TIMER_RUNNING;
  680.                }
  681.             }
  682.          }
  683.          return 0;
  684.  
  685.       case WM_DESTROY:
  686.          StopCaptioning    ( pid );
  687.          DosFreeMem        ( pid );
  688.          break;
  689.    }
  690.  
  691.    return WinDefWindowProc ( hwnd, msg, mp1, mp2 );
  692. }
  693.  
  694. /******************************************************************************
  695.  * Name         :  StartCaptioning
  696.  *
  697.  * Description  :  This procedure will initiate captioning and loads the
  698.  *                 caption file. Initializes internal data structures and the
  699.  *                 caption window.
  700.  *
  701.  * MMPM/2 API's :  mciSendString
  702.  *
  703.  * Parameters   :  pid             Pointer to Instance Data for caption window
  704.  *
  705.  *                 szFilename      Caption file name (NULL terminated)
  706.  *
  707.  *                 hwndOwner       If you want the captioning system to pass
  708.  *                                 position advise messages to one of your
  709.  *                                 windows, pass the handle of that window
  710.  *                                 here.  Otherwise pass NULL.
  711.  *
  712.  *                 szDeviceName    Alias/Device name of the audio device. Pass
  713.  *                                 this so that the caption system can request
  714.  *                                 MM_MCIPOSITIONCHANGE messages from this
  715.  *                                 device.
  716.  *
  717.  * Return       :  0      -  if the operation was initiated without error.
  718.  *                 apiret -  if an error occurred.
  719.  *
  720.  ******************************************************************************/
  721. static APIRET StartCaptioning ( PCCINSTANCE pid, CHAR szFilename[],
  722.                                      HWND hwndOwner, CHAR szDeviceName[] )
  723. {
  724.    APIRET apiret;                     /* Values returned from DOS operations  */
  725.    ULONG  ulBytesReq;                 /* Bytes requested for DosAllocMem calls*/
  726.    CHAR   szCommandString[CCHMAXPATH] =
  727.             "setpositionadvise ";
  728.    LONG   lSendStringRC = 0;
  729.  
  730.  
  731. /*---------- Check for device ID. Return error if not found.------------------*/
  732.  
  733.    if (strlen(szDeviceName) == 0)
  734.       return (APIRET) CCERR_NO_DEVICE_NAME;
  735.  
  736. /*---------- Stop any captioning that is currently underway ------------------*/
  737.  
  738.    if ( pid->ulFlags & CC_CAPTIONS_ACTIVE )
  739.       StopCaptioning ( pid );
  740.    apiret = CreateBitmap( pid );   /* Create the backup bitmap for painting   */
  741.                                            /* captions.                       */
  742.    if (apiret)
  743.       return (APIRET) apiret;
  744.  
  745.  
  746. /*----- Load the caption file, creating Text and Line Data Tables ------------*/
  747.  
  748.    ulBytesReq = (ULONG) ( MAX_TEXT_LINES * sizeof(LINEDATA) );
  749.    DosAllocMem ( &pid->pvLinedataTable, ulBytesReq, fALLOC );
  750.    apiret = LoadCaptionFile ( pid, szFilename );
  751.    if ( apiret )
  752.    {                                       /*---------------------------------*/
  753.       if ( pid->pvText )                   /*                                 */
  754.       {                                    /*  If we were unable to load the  */
  755.          DosFreeMem ( pid->pvText );       /*  caption file, then clean up    */
  756.          pid->pvText = (PVOID) NULL;       /*  any resources we allocated, and*/
  757.       }                                    /*  return an error code.          */
  758.       DosFreeMem ( pid->pvLinedataTable ); /*                                 */
  759.       pid->pvLinedataTable = (PVOID) NULL; /*---------------------------------*/
  760.       return apiret;
  761.    }
  762.  
  763.  
  764. /*---------------- Format the first few lines of text ------------------------*/
  765.  
  766.    FormatTextBitmap ( pid, 0 ); /* Write the first line to text to the bitmap.*/
  767.  
  768.  
  769. /*---------------- Set instance data to initial values -----------------------*/
  770.  
  771.    pid->lScrollOffset = 0;
  772.    pid->usDisplayLine = 0;/* Index of line currently at bottom of the cap. win*/
  773.    pid->usNextlineReq = 0;
  774.    pid->ulTimePrev    = 0;
  775.    pid->hwndPosition  = hwndOwner;
  776.  
  777.  
  778. /*--- Ask MMPM/2 for position advise messages on the device ------------------*/
  779.  
  780.    strcat( szCommandString, szDeviceName);
  781.    strcat( szCommandString, " ");
  782.    strcat( szCommandString, "on every 1500 notify");
  783.  
  784.    lSendStringRC =
  785.         mciSendString( (PSZ)szCommandString,
  786.                        (PSZ)NULL,
  787.                        CCHMAXPATH,
  788.                        pid->hwndCaption,
  789.                        0 );
  790.  
  791.    if (lSendStringRC != 0)
  792.       return (APIRET) lSendStringRC;
  793.  
  794.    PositionCaptionWindow ( pid );
  795.    pid->ulFlags |= CC_CAPTIONS_ACTIVE;
  796.    WinSetWindowPos ( pid->hwndCaption, 0, 0, 0, 0, 0, SWP_SHOW );
  797.  
  798.    return (APIRET) 0;
  799. }
  800.  
  801. /******************************************************************************
  802.  * Name         :  PositionCaptionWindow
  803.  *
  804.  * Description  :  This procedure will position the caption window in its
  805.  *                 parent window and display it.
  806.  *
  807.  * Parameters   :  pid          Pointer to Instance Data for caption window
  808.  *
  809.  * Return       :  none
  810.  *
  811.  ******************************************************************************/
  812. static void PositionCaptionWindow ( PCCINSTANCE pid )
  813. {
  814.    RECTL rclParent;  /* Receives dimensions of the parent of the caption win  */
  815.    LONG  lXcoord;    /* Computed position of caption window - X coordinate    */
  816.    LONG  lYcoord;    /* Computed position of caption window - Y coordinate    */
  817.  
  818.  
  819. /*------------------------ Locic summary -------------------------------------*/
  820. /*                                                                            */
  821. /*  (1) Calculate horizontal position.  If the user has specified a position, */
  822. /*      then use that position.  If not, then center the Captioning Window    */
  823. /*      horizontally in its parent window.                                    */
  824. /*                                                                            */
  825. /*  (2) Calculate the vertical position. If the user has specified a position,*/
  826. /*      then use that position.  If not, then place the captioning window near*/
  827. /*      the bottom of the parent window.                                      */
  828. /*                                                                            */
  829. /*----------------------------------------------------------------------------*/
  830.  
  831.    WinQueryWindowRect ( pid->hwndParent, &rclParent );
  832.  
  833.    if ( pid->ulFlags & CC_USER_POSITION )          /* (1) Horizontal position */
  834.       lXcoord = pid->lWindowXcoord;
  835.    else
  836.    {
  837.       lXcoord = ( rclParent.xRight - pid->rclText.xRight ) / 2;
  838.       pid->lWindowXcoord = lXcoord;
  839.    }
  840.  
  841.    if ( pid->ulFlags & CC_USER_POSITION )          /* (2) Vertical position   */
  842.       lYcoord = pid->lWindowYcoord;
  843.    else
  844.    {
  845.       lYcoord = rclParent.yTop / 12;
  846.       pid->lWindowYcoord = lYcoord;
  847.    }
  848.  
  849.    pid->rclWin = pid->rclText;
  850.  
  851.    WinSetWindowPos( pid->hwndCaption, 0, lXcoord, lYcoord, pid->rclText.xRight,
  852.                             pid->rclText.yTop, SWP_MOVE | SWP_SIZE );
  853. }
  854.  
  855. /******************************************************************************
  856.  * Name         :  CreateBitmap
  857.  *
  858.  * Description  :  This procedure will create a backup text bitmap for the
  859.  *                 captioning window.  Data from the bitmap will be blitted
  860.  *                 to the caption window whenever it receives a WM_PAINT
  861.  *                 message.
  862.  *
  863.  * Parameters   :  pid  -  Pointer to Instance Data for caption window
  864.  *
  865.  * Return       :  none
  866.  *
  867.  ******************************************************************************/
  868. static APIRET CreateBitmap ( PCCINSTANCE pid )
  869. {
  870.    FONTMETRICS        fmText;          /* Font metrics for backup bitmap font */
  871.    SIZEL              sizelText;       /* Needed to initialize the bitmap     */
  872.    BITMAPINFOHEADER2  bmh2;            /* Needed to initialize the bitmap     */
  873.    LONG               lModulo;
  874.    APIRET             apiret;
  875.  
  876.  
  877.    /*----------------------- Logic overview ----------------------------------*/
  878.    /*                                                                         */
  879.    /*  (1) If a backup bitmap already exists for this window, destroy it      */
  880.    /*      before creating the new bitmap.  (We can't just use the old bitmap */
  881.    /*      because the user might have decided to resize the window.)         */
  882.    /*                                                                         */
  883.    /*  (2) Open a new device context and create a new presentation space for  */
  884.    /*      the backup bitmap.                                                 */
  885.    /*                                                                         */
  886.    /*  (3) Get the font metrics for the default font for this presentation    */
  887.    /*      space. Use this data in conjunction with the window instance data  */
  888.    /*      (usWindowLines, usWindowChars) to determine the dimensions of the  */
  889.    /*      bitmap.  The bitmap will be taller than the window by the height   */
  890.    /*      of one text line.  This is so the caption window can be repainted  */
  891.    /*      correctly while it is scrolling.                                   */
  892.    /*                                                                         */
  893.    /*  (4) Create the backup bitamp.  This is a monochrome bitmap; the colors */
  894.    /*      of the text and background are determined during the WM_PAINT      */
  895.    /*      message for the caption window.                                    */
  896.    /*                                                                         */
  897.    /*-------------------------------------------------------------------------*/
  898.  
  899.    if ( pid->hpsText )
  900.    {
  901.       GpiDestroyPS   ( pid->hpsText );/*--------------------------------------*/
  902.       DevCloseDC     ( pid->hdcText );/* (1)Destroy any existing backup bitmap*/
  903.       GpiDeleteBitmap( pid->hbmText );/*--------------------------------------*/
  904.    }
  905.  
  906.  
  907.    /*--------- (2) Open device context and create presentation space ---------*/
  908.  
  909.    pid->hdcText           = DevOpenDC ( pid->hab, OD_MEMORY, "*", 0, NULL, 0 );
  910.  
  911.    sizelText.cx           = 0;
  912.    sizelText.cy           = 0;
  913.    pid->hpsText           = GpiCreatePS( pid->hab, pid->hdcText, &sizelText,
  914.                               PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC);
  915.  
  916.  
  917.    /*--------- (3) Use the font metrics to determine size of bitmap ----------*/
  918.  
  919.    GpiQueryFontMetrics ( pid->hpsText, (LONG) sizeof(fmText), &fmText );
  920.  
  921.    pid->lLineSpacing  = fmText.lMaxBaselineExt       /*-----------------------*/
  922.                       + fmText.lExternalLeading      /* Force the line spacing*/
  923.                       + pid->lScrollInc;             /* to be a multiple of   */
  924.    if (lModulo = pid->lLineSpacing % pid->lScrollInc)/* the scroll increment. */
  925.       pid->lLineSpacing += pid->lScrollInc - lModulo;/*-----------------------*/
  926.  
  927.    pid->rclText.yTop  = pid->lLineSpacing          /* Height of caption window*/
  928.                       * (LONG) pid->usWindowLines  /* ( The bitmap is one text*/
  929.                       + pid->lScrollInc;           /* line greater in height.)*/
  930.  
  931.    pid->rclText.xRight =  fmText.lAveCharWidth * pid->usWindowChars;
  932.    pid->lBottomLineY  = pid->rclText.yTop -
  933.                         ( pid->lLineSpacing * pid->usWindowLines ) +
  934.                         fmText.lMaxDescender;
  935.  
  936.  
  937.    /*--------------------- (4) Create the bitmap -----------------------------*/
  938.  
  939.    bmh2.cbFix           = sizeof (BITMAPINFOHEADER2);
  940.    bmh2.cx              = pid->rclText.xRight;
  941.    bmh2.cy              = pid->rclText.yTop+pid->lLineSpacing;/* Bitmap height*/
  942.    bmh2.cPlanes         = 1;
  943.    bmh2.cBitCount       = 1;                                    /* Monochrome */
  944.    bmh2.ulCompression   = BCA_UNCOMP;
  945.    bmh2.cbImage         = 0;
  946.    bmh2.cxResolution    = 30000;
  947.    bmh2.cyResolution    = 30000;
  948.    bmh2.cclrUsed        = 2;
  949.    bmh2.cclrImportant   = 2;
  950.    bmh2.usUnits         = BRU_METRIC;
  951.    bmh2.usReserved      = 0;
  952.    bmh2.usRecording     = BRA_BOTTOMUP;
  953.    bmh2.usRendering     = BRH_NOTHALFTONED;
  954.    bmh2.cSize1          = 0;
  955.    bmh2.cSize2          = 0;
  956.    bmh2.ulColorEncoding = BCE_RGB;
  957.    bmh2.ulIdentifier    = 0;
  958.    pid->hbmText         = GpiCreateBitmap(pid->hpsText, &bmh2, 0L, 0L, NULL);
  959.  
  960.    if (pid->hbmText == 0)
  961.       return (APIRET) CCERR_CANNOT_CREATE_BITMAP;
  962.  
  963.    GpiSetBitmap           ( pid->hpsText, pid->hbmText );
  964.    GpiErase               ( pid->hpsText );
  965.  
  966.    return(APIRET) 0;
  967. }
  968.  
  969. /******************************************************************************
  970.  * Name         :  FormatTextBitmap
  971.  *
  972.  * Description  :  This procedure will write text to the backup bitmap beginning
  973.  *                 with the bottom line and working up.
  974.  *
  975.  * Parameters   :  pid        -  Pointer to Instance Data for caption window
  976.  *                 usLastLine - Index of bottom-most text line in bitmap.
  977.  *
  978.  * Return       :  none
  979.  *
  980.  ******************************************************************************/
  981. static VOID FormatTextBitmap ( PCCINSTANCE pid, USHORT usLastLine )
  982. {
  983.    PLINEDATA pld;        /* Pointer to LINEDATA structure for current line    */
  984.    POINTL    ptlText;    /* Coordinate of text                                */
  985.    USHORT    usCurrLine; /* Index of line currently being written to bitmap   */
  986.    USHORT    usLineCount;/* Counter for number of lines written to text bitmap*/
  987.  
  988.  
  989.    GpiErase ( pid->hpsText );
  990.    pld        = (PLINEDATA) pid->pvLinedataTable + usLastLine;
  991.    ptlText.x  = pid->lLeftMargin;
  992.    ptlText.y  = pid->lBottomLineY;
  993.    usCurrLine = usLastLine;
  994.  
  995.    for ( usLineCount = 0; usLineCount <= pid->usWindowLines; usLineCount++ )
  996.    {
  997.       GpiCharStringAt(pid->hpsText, &ptlText, pld->lTextLen, (PCH)pld->szText);
  998.       if ( usCurrLine == 0 ) break;
  999.       ptlText.y += pid->lLineSpacing;
  1000.       pld--;
  1001.       usCurrLine--;
  1002.    }
  1003. }
  1004.  
  1005. /******************************************************************************
  1006.  * Name         :  LoadCaptionFile
  1007.  *
  1008.  * Description  :  This procedure will load a caption file for processing.
  1009.  *
  1010.  * Parameters   :  pid           -  Pointer to Instance Data for caption window
  1011.  *                 szCaptionFile -  NULL-terminated caption file name.
  1012.  *
  1013.  * Return       :  0      -  if the operation was initiated without error.
  1014.  *                 apiret -  if an error occurred.
  1015.  *
  1016.  ******************************************************************************/
  1017. static APIRET LoadCaptionFile( PCCINSTANCE pid, CHAR szCaptionFile[] )
  1018. {
  1019.    APIRET    apiret;          /* DOS return code                              */
  1020.    HFILE     hfile;           /* Text file handle                             */
  1021.    ULONG     ulAction;        /* Parameter returned by DOS API functions      */
  1022.    ULONG     ulFileSize;      /* Expected file size, used as a read check     */
  1023.    ULONG     ulBytesRemaining;/* Bytes in file remaining to be scanned        */
  1024.    PLINEDATA pld;             /* Points to "current" line in Line Data Table  */
  1025.    UCHAR     ucEndChar;       /* Used to save char immed. following MM time   */
  1026.    PSZ       psz;             /* Pointer to "current line" in caption file    */
  1027.  
  1028.    PVOID     pvPositions;     /* Points to table used by GpiQueryCharPosAt    */
  1029.    PPOINTL   pptl;            /* (Same as above, but cast to a  useful type)  */
  1030.    POINTL    ptlStart;        /* Set to co-or of leftmost char in caption win */
  1031.    PLINEDATA pldNew;          /* Points to "remainder" of line after word-wrap*/
  1032.    SHORT     sIndex;        /* Used to scan line for a logical breaking point.*/
  1033.    PSZ       pszEnd;        /* Points to first memory position after text data*/
  1034.  
  1035. /*----------------------------------------------------------------------------*/
  1036.  
  1037.    if ( pid->pvText )              /*-----------------------------------------*/
  1038.    {                               /*                                         */
  1039.       DosFreeMem ( pid->pvText );  /* Free any text that is currently loaded. */
  1040.       pid->pvText = NULL;          /*                                         */
  1041.    }                               /*-----------------------------------------*/
  1042.  
  1043.  
  1044. /*----------------------- Open the text file ---------------------------------*/
  1045.  
  1046.    apiret = DosOpen ( (PSZ) szCaptionFile, &hfile, &ulAction, 0,
  1047.                       FILE_NORMAL | FILE_READONLY, FILE_OPEN,
  1048.                       OPEN_ACCESS_READONLY | OPEN_SHARE_DENYREADWRITE, NULL );
  1049.  
  1050.    if ( apiret ) return apiret;
  1051.  
  1052.  
  1053. /*------------ Read the entire file into a memory area -----------------------*/
  1054.  
  1055.    DosSetFilePtr ( hfile, 0, FILE_END, &ulFileSize ); /* Determine file size. */
  1056.    apiret =
  1057.       DosAllocMem( &pid->pvText, ulFileSize, fALLOC );/* Alloc memory for it. */
  1058.  
  1059.    if ( apiret)
  1060.    {
  1061.       DosClose ( hfile );
  1062.       return apiret;
  1063.    }
  1064.  
  1065.    DosSetFilePtr ( hfile, 0, FILE_BEGIN, &ulAction );/* Read file image into */
  1066.    apiret = DosRead ( hfile, pid->pvText, ulFileSize, &ulAction );/* buffer. */
  1067.    DosClose ( hfile );
  1068.    if ( ! apiret && ( ulFileSize != ulAction ) ) apiret = CCERR_UNEXPECTED_EOF;
  1069.    pszEnd = (PSZ) pid->pvText + ulFileSize;
  1070.  
  1071.    if ( apiret ) return apiret;
  1072.  
  1073.  
  1074. /*----------------------------------------------------------------------------*/
  1075. /*  Make sure the file is in the correct format, by checking the first line   */
  1076. /*  for the constant "CC_FILE".                                               */
  1077. /*----------------------------------------------------------------------------*/
  1078.  
  1079.    if ( strncmp( (char *) pid->pvText, "CC_FILE", 7 ) )/* Check for format ID */
  1080.       return(APIRET) CCERR_FILE_FORMAT;                /* Return error if not */
  1081.                                                        /* found.              */
  1082.    psz = (PSZ) memchr ( pid->pvText, CHAR_RETURN, (size_t) ulFileSize );
  1083.    if ( psz == NULL ) return(APIRET) CCERR_FILE_FORMAT;/* Bump pointer psz    */
  1084.    psz += 2;                                           /* to the start of the */
  1085.    if ( ( *psz == CHAR_EOF ) || ( psz >= pszEnd ) )    /* first line of data. */
  1086.       return (APIRET) CCERR_FILE_FORMAT;
  1087.  
  1088.  
  1089. /*----------------------------------------------------------------------------*/
  1090. /*  Allocate memory to hold a character-position table. This table will be    */
  1091. /*  used to determine if and when it will be necessary to split a line to fit */
  1092. /*  into the captioning window.                                               */
  1093. /*----------------------------------------------------------------------------*/
  1094.  
  1095.    DosAllocMem ( &pvPositions, 256 * sizeof(POINTL), fALLOC );
  1096.    pptl       = (PPOINTL) pvPositions;
  1097.    ptlStart.x = pid->lLeftMargin;                      /* Get memory for char */
  1098.    ptlStart.y = pid->lBottomLineY;                     /* position table.     */
  1099.  
  1100.  
  1101. /*----------------------------------------------------------------------------*/
  1102. /*                                                                            */
  1103. /*  Now loop through every line in the caption file.  For each line build an  */
  1104. /*  entry in the Line Data Table.  There will be AT LEAST as many entries in  */
  1105. /*  the Line Data Table as there are lines in the caption file.               */
  1106. /*                                                                            */
  1107. /*  (1) The first eight characters of the line is an ASCII number representing*/
  1108. /*      the time ( in MM units ) when the caption is to be scrolled into the  */
  1109. /*      window. The text (if any) begins in column ten.                       */
  1110. /*                                                                            */
  1111. /*  (2) The caption text runs from column ten to the next carriage return.    */
  1112. /*      Determine its length and place that in pld->lTextLen.                 */
  1113. /*                                                                            */
  1114. /*  (3) Wrap text lines that are wider than the caption window.  Create extra */
  1115. /*      entries in the Line Data Table for the extra lines that are needed.   */
  1116. /*                                                                            */
  1117. /*----------------------------------------------------------------------------*/
  1118.  
  1119.    pld = (PLINEDATA) pid->pvLinedataTable;
  1120.    for ( pid->usLineCount = 1; pid->usLineCount < MAX_TEXT_LINES;
  1121.                     pid->usLineCount++ )
  1122.    {
  1123.       ucEndChar     = psz[8];                   /*----------------------------*/
  1124.       psz[8]        = CHAR_NULL;                /* (1) Convert the time to a  */
  1125.       pld->ulTime   = (ULONG) atol((PCHAR) psz);/*  long integer and setup a  */
  1126.       pld->szText   = psz + 9;                  /*  default text len of zero  */
  1127.       pld->lTextLen = 0;                        /*----------------------------*/
  1128.  
  1129.       if ( ucEndChar != CHAR_RETURN )        /* (2) If text present, find its */
  1130.       {                                      /*     length.                   */
  1131.          ulBytesRemaining = (ULONG) ( (ULONG) pszEnd - (ULONG) psz );
  1132.          psz =
  1133.            (PSZ) memchr( pld->szText, CHAR_RETURN, (size_t) ulBytesRemaining );
  1134.          if ( psz == NULL ) break;
  1135.          *psz = CHAR_NULL;
  1136.          pld->lTextLen = (LONG) strlen ( pld->szText );
  1137.          psz += 2;
  1138.  
  1139.          /*-------------------------------------------------------------------*/
  1140.          /*                                                                   */
  1141.          /* (3) Wrap text lines that are too wide to fit into the caption     */
  1142.          /*     window. The overall strategy of this logic is as follows:  If */
  1143.          /*     the line istoo wide, divide it into two parts--a part that    */
  1144.          /*     will fit and a part which contains everything else; then      */
  1145.          /*     apply this same strategy to the second part of the line. Keep */
  1146.          /*     repeating this logic until we are left a line which will fit  */
  1147.          /*     without any left-over parts.                                  */
  1148.          /*                                                                   */
  1149.          /*     In detail:                                                    */
  1150.          /*                                                                   */
  1151.          /*        (3a) Determine the width of the current line. If it fits   */
  1152.          /*             in the caption window, we are done!  Otherwise...     */
  1153.          /*                                                                   */
  1154.          /*        (3b) We will have to divide the line.  Make a new entry in */
  1155.          /*             the Line Data Table.  We will use this for the second */
  1156.          /*             part of the line.                                     */
  1157.          /*                                                                   */
  1158.          /*        (3c) Loop backwards through the Character Position Table   */
  1159.          /*             until we find the last space character that will fit  */
  1160.          /*             in the window. (If there are no blanks in the first   */
  1161.          /*             portion, just chop the line off at the window width   */
  1162.          /*             for a "Procrustean" fit.)                             */
  1163.          /*                                                                   */
  1164.          /*        (3d) Make the second portion of the original line the new  */
  1165.          /*             "current" line and repeat the operation.              */
  1166.          /*                                                                   */
  1167.          /*-------------------------------------------------------------------*/
  1168.  
  1169.          for ( ; ; )
  1170.          {                                           /* (3a) Does it fit yet?*/
  1171.             GpiQueryCharStringPosAt( pid->hpsText, &ptlStart, 0, pld->lTextLen,
  1172.                                                (PCH) pld->szText, NULL, pptl );
  1173.             if ( pptl[pld->lTextLen].x <= pid->rclText.xRight )
  1174.                break;
  1175.  
  1176.             pid->usLineCount++;                   /*--------------------------*/
  1177.             if (pid->usLineCount>=MAX_TEXT_LINES) /* (3b) Make sure there is  */
  1178.             {                                     /* enough room in the Line  */
  1179.                DosFreeMem ( pvPositions );        /* Data Table for new line. */
  1180.                return(APIRET)CCERR_TOO_MANY_LINES;/* If so, use the existing  */
  1181.             }                                     /* Line Data entry as the   */
  1182.             pldNew  = pld + 1;                    /* basis for the new one.   */
  1183.             *pldNew = *pld;                       /*--------------------------*/
  1184.  
  1185.             for ( sIndex = pld->lTextLen; sIndex > 0; sIndex-- )
  1186.                if ( pptl[sIndex].x <= pid->rclText.xRight ) break;
  1187.             pld->lTextLen = (LONG) sIndex;
  1188.             sIndex--;                             /*--------------------------*/
  1189.             while ( sIndex > 0 )                  /* (3c) Truncate existing   */
  1190.             {                                     /* line at the last space   */
  1191.                if ( pld->szText[sIndex] == ' ' )  /* that will fit.           */
  1192.                   break;                          /*--------------------------*/
  1193.                sIndex--;
  1194.             }
  1195.             if ( sIndex ) pld->lTextLen = (LONG) sIndex + 1;
  1196.  
  1197.             /*-----------------------------------------------*/
  1198.             /* (3d) Begin new line where old line ends.      */
  1199.             /*-----------------------------------------------*/
  1200.             pldNew->lTextLen -= pld->lTextLen;
  1201.             pldNew->szText = pld->szText + pld->lTextLen;
  1202.             pld = pldNew;
  1203.           }
  1204.       }
  1205.       else
  1206.          psz += 10;       /* (Take this branch of the if in case there */
  1207.                               /*   is no caption text) */
  1208.  
  1209. /*------------ Break out if the loop when we reach end of file ---------------*/
  1210.  
  1211.       if ( ( *psz == CHAR_EOF ) || ( psz >= pszEnd ) )
  1212.          break;
  1213.  
  1214.       pld++;
  1215.    }
  1216.  
  1217.    DosFreeMem( pvPositions );/* Free memory used for the Char. Position Table */
  1218.  
  1219.  
  1220. /*---------------------------- Make some final checks ------------------------*/
  1221. /*                                                                            */
  1222. /*   (1) Make sure the file ends in a standard EOF character.                 */
  1223. /*   (2) See if we had enough room in the Line Data Table to hold the file.   */
  1224. /*----------------------------------------------------------------------------*/
  1225.  
  1226.    if ( psz == NULL )
  1227.       return (APIRET) CCERR_FILE_FORMAT;
  1228.  
  1229.    if ( pid->usLineCount == MAX_TEXT_LINES )
  1230.       return (APIRET) CCERR_TOO_MANY_LINES;
  1231.  
  1232.    return 0;
  1233. }
  1234.  
  1235. /******************************************************************************
  1236.  * Name         :  StopCaptioning
  1237.  *
  1238.  * Description  :  This procedure will stop the current captioning operation.
  1239.  *
  1240.  * Parameters   :  pid   -  Pointer to Instance Data for caption window
  1241.  *
  1242.  * Return       :  none
  1243.  *
  1244.  ******************************************************************************/
  1245. static VOID StopCaptioning ( PCCINSTANCE pid )
  1246. {
  1247.    pid->ulFlags &= ~CC_CAPTIONS_ACTIVE;
  1248.    WinSetWindowPos ( pid->hwndCaption, 0, 0, 0, 0, 0, SWP_HIDE );
  1249.    if ( pid->ulFlags & CC_TIMER_RUNNING )
  1250.    {
  1251.       WinStopTimer ( pid->hab, pid->hwndCaption, CC_TIMER_ID );
  1252.       pid->ulFlags &= ~CC_TIMER_RUNNING;
  1253.    }
  1254.    if ( pid->hpsText )
  1255.    {
  1256.       GpiDestroyPS    ( pid->hpsText ); /*------------------------------------*/
  1257.       DevCloseDC      ( pid->hdcText ); /* Destroy any existing backup bitmap */
  1258.       GpiDeleteBitmap ( pid->hbmText ); /*------------------------------------*/
  1259.       pid->hpsText = (HPS) 0;
  1260.    }
  1261.    if ( pid->pvText )
  1262.    {
  1263.       DosFreeMem ( pid->pvText );
  1264.       pid->pvText = (PVOID) NULL;
  1265.    }
  1266.    if ( pid->pvLinedataTable )
  1267.    {
  1268.       DosFreeMem ( pid->pvLinedataTable );
  1269.       pid->pvLinedataTable = (PVOID) NULL;
  1270.    }
  1271. }
  1272.