home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / video / aviedit / aviedit.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-10-05  |  100.3 KB  |  3,378 lines

  1. /****************************************************************************
  2.  *
  3.  *  AVIEDIT.C
  4.  *
  5.  *  Sample program using the AVIFile read/write routines
  6.  *
  7.  **************************************************************************/
  8. /**************************************************************************
  9.  *
  10.  *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  11.  *  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  12.  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  13.  *  PURPOSE.
  14.  *
  15.  *  Copyright (C) 1992 - 1997 Microsoft Corporation.  All Rights Reserved.
  16.  *
  17.  **************************************************************************/
  18.  
  19. #define STRICT
  20. #define INC_OLE2
  21. #include <windows.h>
  22. #include <shellapi.h>
  23. #include <windowsx.h>
  24. #include <commdlg.h>
  25. #include "muldiv32.h"
  26. #include <vfw.h>
  27.  
  28. #include "aviedit.h"
  29. #include "audio.h"
  30.  
  31. #include <limits.h>
  32.  
  33. #define GlobalSizePtr(lp)   GlobalSize(GlobalPtrHandle(lp))
  34. #define LPPAVIFILE PAVIFILE *
  35. typedef BYTE * HPBYTE;
  36. typedef UNALIGNED short * HPSHORT;
  37.  
  38. /*----------------------------------------------------------------------------*\
  39. \*----------------------------------------------------------------------------*/
  40. static BOOL gfDefDlgEx = FALSE;         //the recursion flag for message crackers
  41.  
  42. #define BUFSIZE 260
  43. static char gszBuffer[BUFSIZE];
  44. static char gszFileName[BUFSIZE];
  45. static char gszSaveFileName[BUFSIZE];
  46.  
  47. #define AVI_EDIT_CLASS  "edit"
  48. static LPAVISTREAMINFO glpavisi;
  49. static int gnSel;
  50. int     gSelectedStream = -1;   // Highlight this text area when painting
  51. RECT    grcSelectedStream;      // where to highlight
  52.  
  53. char    gszFilter[512];     // for AVIBuildFilter - more than one string!
  54. static  HINSTANCE   ghInstApp;
  55. static  HWND        ghwndApp;
  56. static  HACCEL      ghAccel;
  57. static  WNDPROC     gOldEditProc;
  58. static  HWND        ghwndEdit;
  59.  
  60. #define SCROLLRANGE  10000
  61. #define MAXNUMSTREAMS   25
  62.  
  63. int                 gcpavi;                     // # of streams
  64. PAVISTREAM          gapavi[MAXNUMSTREAMS];      // the current streams
  65. int                 gcpaviSel;                  // num of edit streams
  66. PAVISTREAM          gapaviSel[MAXNUMSTREAMS];   // edit streams to put on clipbd
  67. int                 gStreamTop[MAXNUMSTREAMS+1];// y position of each stream
  68. AVICOMPRESSOPTIONS  gaAVIOptions[MAXNUMSTREAMS];// compression options
  69. LPAVICOMPRESSOPTIONS  galpAVIOptions[MAXNUMSTREAMS];
  70. PGETFRAME           gapgf[MAXNUMSTREAMS];       // data for decompressing video
  71. HDRAWDIB            ghdd[MAXNUMSTREAMS];        // drawdib handles
  72. LONG                galSelStart[MAXNUMSTREAMS];
  73. LONG                galSelLen[MAXNUMSTREAMS];
  74. int                 giFirstAudio = -1;          // 1st audio stream found
  75. int                 giFirstVideo = -1;          // 1st video stream found
  76.  
  77. #define             gfVideoFound (giFirstVideo >= 0)
  78. #define             gfAudioFound (giFirstAudio >= 0)
  79.  
  80. BOOL                gfPlaying;          // are we currently playing?
  81. LONG                glPlayStartTime;    // When did we start playing?
  82. LONG                glPlayStartPos;     // Where were we on the scrollbar?
  83. LONG                timeStart;          // cached start, end, length
  84. LONG                timeEnd;
  85. LONG                timeLength;
  86. LONG                timehscroll;        // how much arrows scroll HORZ bar
  87. int                 nVertSBLen;         // vertical scroll bar
  88. int                 nVertHeight;
  89. DWORD               gdwMicroSecPerPixel = 1000L;        // scale for video
  90. WORD                gwZoom = 2;         // one-half zoom (divide by 4)
  91. HWND                ghwndMCI;
  92.  
  93. // buffer for wave data
  94. LPVOID lpAudio;
  95.  
  96. // constants for painting
  97. #define VSPACE  8       // some vertical spacing
  98. #define SELECTVSPACE 4 // height of selection line
  99. #define HSPACE  4       // space between frames for video stream
  100. #define TSPACE  10      // space for text area about each stream
  101. #define AUDIOVSPACE  64 // height of an audio stream at X1 zoom
  102.  
  103. #define HIGHLIGHT       (GetSysColor(COLOR_HIGHLIGHT) ? GetSysColor(COLOR_HIGHLIGHT) : GetSysColor(COLOR_ACTIVECAPTION))
  104.  
  105. /*----------------------------------------------------------------------------*\
  106. \*----------------------------------------------------------------------------*/
  107.  
  108. // Macros to get and set the scroll bar to a given millisecond value in the
  109. // movie.  Movie lengths can be DWORDS but we only have 16 bits of resolution.
  110.  
  111. #define GetScrollTime(hwnd) \
  112. (timeStart + muldiv32(GetScrollPos(hwnd, SB_HORZ), timeLength, SCROLLRANGE))
  113.  
  114. #define SetScrollTime(hwnd, time) SetScrollPos(hwnd, SB_HORZ, \
  115. (int)muldiv32((time) - timeStart, SCROLLRANGE, timeLength), TRUE)
  116.  
  117. /*----------------------------------------------------------------------------*\
  118. \*----------------------------------------------------------------------------*/
  119.  
  120. LRESULT CALLBACK AppWndProc(HWND, UINT, WPARAM, LPARAM );
  121. LRESULT CALLBACK NewEditProc(HWND, UINT, WPARAM, LPARAM );
  122. BOOL CALLBACK    AboutDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  123. int  ErrMsg (LPSTR sz,...);
  124.  
  125. BOOL MenuHandler( HWND, int );
  126.  
  127. void editPaste(HWND hwnd, PAVIFILE pfile);
  128. void             FrameVideo(HDC hdc, RECT *rcFrame, HBRUSH hbr);
  129.  
  130. /*----------------------------------------------------------------------------*\
  131. \*----------------------------------------------------------------------------*/
  132. static int gfWait = 0;
  133.  
  134. /*----------------------------------------------------------------------------*\
  135. |    StartWait()                                                                |
  136. |    Start a wait operation... put up the hourglass if it's the first call      |
  137. \*----------------------------------------------------------------------------*/
  138. void StartWait()
  139. {
  140.     if (gfWait++ == 0) {
  141.     SetCursor(LoadCursor(NULL,IDC_WAIT));
  142.     }
  143. }
  144.  
  145. /*----------------------------------------------------------------------------*\
  146. |    EndWait()                                                                  |
  147. |    Once every one who started a wait is finished, go back to regular cursor   |
  148. \*----------------------------------------------------------------------------*/
  149. void EndWait()
  150. {
  151.     if (--gfWait == 0) {
  152.     SetCursor(LoadCursor(NULL,IDC_ARROW));
  153.     InvalidateRect(ghwndApp, NULL, TRUE);
  154.     }
  155. }
  156.  
  157. /*----------------------------------------------------------------------------*\
  158. |    WinYield()                                                                 |
  159. |    Code to yield while we're not calling GetMessage.                          |
  160. |    Dispatch all messages.  Pressing ESC or closing aborts.                    |
  161. \*----------------------------------------------------------------------------*/
  162.  
  163. BOOL WinYield(void)
  164. {
  165.     MSG msg;
  166.     BOOL fAbort=FALSE;
  167.  
  168.     while(gfWait > 0 && PeekMessage(&msg,NULL,0,0,PM_REMOVE))
  169.     {
  170.     if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE)
  171.         fAbort = TRUE;
  172.     if (msg.message == WM_SYSCOMMAND && (msg.wParam & 0xFFF0) == SC_CLOSE)
  173.         fAbort = TRUE;
  174.     TranslateMessage(&msg);
  175.     DispatchMessage(&msg);
  176.     }
  177.     return fAbort;
  178. }
  179.  
  180.  
  181. /*----------------------------------------------------------------------------*\
  182. |    PaintVideo()                                                               |
  183. |    Draw a video frame in the specified rect                                   |
  184. \*----------------------------------------------------------------------------*/
  185.  
  186. void PaintVideo(HDC hdc, RECT rcFrame, int iStream, LPBITMAPINFOHEADER lpbi, LONG lCurSamp, LONG lPos)
  187. {
  188.     int         iLen;
  189.     COLORREF    nCol;
  190.     char        szText[BUFSIZE];
  191.     RECT        rc;
  192.  
  193.     //
  194.     // If we have a picture, draw it
  195.     //
  196.     if (lpbi)
  197.     {
  198.     //
  199.     // use the palette of the first video stream
  200.     //
  201.     DrawDibDraw(ghdd[iStream], hdc,
  202.             rcFrame.left, rcFrame.top,
  203.             rcFrame.right - rcFrame.left,
  204.             rcFrame.bottom - rcFrame.top,
  205.             lpbi, NULL,
  206.             0, 0, -1, -1,
  207.             (iStream == giFirstVideo) ? 0 :DDF_BACKGROUNDPAL);
  208.  
  209.     iLen = wsprintf(szText, "%ld %ld.%03lds",
  210.             lCurSamp, lPos / 1000, lPos % 1000);
  211.     }
  212.  
  213.     //
  214.     // Before or after the movie (or read error) draw GRAY
  215.     //
  216.     else {
  217.     SelectObject(hdc,GetStockObject(DKGRAY_BRUSH));
  218.  
  219.     PatBlt(hdc,
  220.            rcFrame.left, rcFrame.top,
  221.            rcFrame.right - rcFrame.left,
  222.            rcFrame.bottom - rcFrame.top,
  223.            PATCOPY);
  224.     iLen = 0;
  225.     szText[0] = '\0';
  226.     }
  227.  
  228.     //
  229.     // print something meaningful under the frame
  230.     //
  231.     rc.left = rcFrame.left;
  232.     rc.right = rcFrame.right - 2 * HSPACE;      // don't overlap text areas
  233.     rc.top = rcFrame.bottom + HSPACE;
  234.     rc.bottom = rc.top + TSPACE + TSPACE;       // blank out enough space
  235.     nCol = SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
  236.     ExtTextOut(hdc, rc.left, rc.top, ETO_CLIPPED | ETO_OPAQUE,
  237.            &rc, szText, iLen, NULL);
  238.     SetBkColor(hdc, nCol);
  239. }
  240.  
  241.  
  242. /*----------------------------------------------------------------------------*\
  243. |    PaintAudio()                                                               |
  244. |    Draw some samples of audio inside the given rectangle                      |
  245. \*----------------------------------------------------------------------------*/
  246.  
  247. void PaintAudio(HDC hdc, PRECT prc, PAVISTREAM pavi, LONG lStart, LONG lLen)
  248. {
  249.     PCMWAVEFORMAT wf;
  250.     int i;
  251.     int x,y;
  252.     int w,h;
  253.     BYTE b;
  254.     HBRUSH hbr;
  255.     RECT rc = *prc;
  256.     LONG    lBytes;
  257.     LONG    l, lLenOrig = lLen;
  258.     LONG    lWaveBeginTime = AVIStreamStartTime(pavi);
  259.     LONG    lWaveEndTime   = AVIStreamEndTime(pavi);
  260.  
  261.     //
  262.     // We can't draw before the beginning of the stream - adjust
  263.     //
  264.     if (lStart < lWaveBeginTime) {
  265.     lLen -= lWaveBeginTime - lStart;
  266.     lStart = lWaveBeginTime;
  267.     // right justify the legal samples in the rectangle - don't stretch
  268.     rc.left = rc.right - (int)muldiv32(rc.right - rc.left, lLen, lLenOrig);
  269.     }
  270.  
  271.     //
  272.     // We can't draw past the end of the stream
  273.     //
  274.     if (lStart + lLen > lWaveEndTime) {
  275.     lLenOrig = lLen;
  276.     lLen = max(0, lWaveEndTime - lStart);   // maybe nothing to draw!
  277.     // left justify the legal samples in the rectangle - don't stretch
  278.     rc.right = rc.left + (int)muldiv32(rc.right - rc.left, lLen, lLenOrig);
  279.     }
  280.  
  281.     // Now start working with samples, not time
  282.     l = lStart;
  283.     lStart = AVIStreamTimeToSample(pavi, lStart);
  284.     lLen = AVIStreamTimeToSample(pavi, l + lLen) - lStart;
  285.  
  286.     //
  287.     // Get the format of the wave data
  288.     //
  289.     l = sizeof(wf);
  290.     AVIStreamReadFormat(pavi, lStart, &wf, &l);
  291.     if (!l)
  292.     return;
  293.  
  294.     w = rc.right - rc.left;
  295.     h = rc.bottom - rc.top;
  296.  
  297.     //
  298.     // We were starting before the beginning or continuing past the end.
  299.     // We're not painting in the whole original rect --- use a dark background
  300.     //
  301.     if (rc.left > prc->left) {
  302.     SelectObject(hdc, GetStockObject(DKGRAY_BRUSH));
  303.     PatBlt(hdc, prc->left, rc.top, rc.left - prc->left,
  304.            rc.bottom - rc.top, PATCOPY);
  305.     }
  306.     if (rc.right < prc->right) {
  307.     SelectObject(hdc, GetStockObject(DKGRAY_BRUSH));
  308.     PatBlt(hdc, rc.right, rc.top, prc->right - rc.right,
  309.            rc.bottom - rc.top, PATCOPY);
  310.     }
  311.  
  312. #define BACKBRUSH  (GetSysColor(COLOR_BTNFACE))         // background
  313. #define MONOBRUSH  (GetSysColor(COLOR_BTNSHADOW))       // for mono audio
  314. #define LEFTBRUSH  (RGB(0,0,255))                       // left channel
  315. #define RIGHTBRUSH (RGB(0,255,0))                       // right channel
  316. #define HPOSBRUSH  (RGB(255,0,0))                       // current position
  317.  
  318.     //
  319.     // Paint the background
  320.     //
  321.     hbr = SelectObject(hdc, CreateSolidBrush(BACKBRUSH));
  322.     PatBlt(hdc, rc.left, rc.top, w, h, PATCOPY);
  323.     DeleteObject(SelectObject(hdc, hbr));
  324.  
  325.     //
  326.     // !!! we can only paint PCM data right now.  Sorry!
  327.     //
  328.     if (wf.wf.wFormatTag != WAVE_FORMAT_PCM)
  329.     return;
  330.  
  331.     //
  332.     // How many bytes are we painting? Alloc some space for them
  333.     //
  334.     lBytes = lLen * wf.wf.nChannels * wf.wBitsPerSample / 8;
  335.     if (!lpAudio)
  336.     lpAudio = GlobalAllocPtr (GHND, lBytes);
  337.     else if ((LONG)GlobalSizePtr(lpAudio) < lBytes)
  338.     lpAudio = GlobalReAllocPtr(lpAudio, lBytes, GMEM_MOVEABLE);
  339.     if (!lpAudio)
  340.     return;
  341.  
  342.     //
  343.     // Read in the wave data
  344.     //
  345.     AVIStreamRead(pavi, lStart, lLen, lpAudio, lBytes, NULL, &l);
  346.     if (l != lLen)
  347.     return;
  348.  
  349. #define MulDiv(a,b,c) (UINT)((DWORD)(UINT)(a) * (DWORD)(UINT)(b) / (UINT)(c))
  350.  
  351.     //
  352.     // !!! Flickers less painting it NOW or LATER?
  353.     // First show the current position as a bar
  354.     //
  355.     hbr = SelectObject(hdc, CreateSolidBrush(HPOSBRUSH));
  356.     PatBlt(hdc, prc->right / 2, prc->top, 1, prc->bottom - prc->top, PATCOPY);
  357.     DeleteObject(SelectObject(hdc, hbr));
  358.  
  359.     //
  360.     // Paint monochrome wave data
  361.     //
  362.     if (wf.wf.nChannels == 1) {
  363.  
  364.     //
  365.     // Draw the x-axis
  366.     //
  367.     hbr = SelectObject(hdc, CreateSolidBrush(MONOBRUSH));
  368.     y = rc.top + h/2;
  369.     PatBlt(hdc, rc.left, y, w, 1, PATCOPY);
  370.  
  371.     //
  372.     // 8 bit data is centred around 0x80
  373.     //
  374.     if (wf.wBitsPerSample == 8) {
  375.         for (x=0; x<w; x++) {
  376.  
  377.         // which byte of audio data belongs at this pixel?
  378.         b = *((HPBYTE)lpAudio + muldiv32(x, lLen, w));
  379.  
  380.         if (b > 0x80) {
  381.             i = y - MulDiv(b - 0x80, (h / 2), 128);
  382.             PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  383.         }
  384.         else {
  385.             i = y + MulDiv(0x80 - b, (h / 2), 128);
  386.             PatBlt(hdc, rc.left + x, y, 1, i - y, PATCOPY);
  387.         }
  388.         }
  389.     }
  390.  
  391.     //
  392.     // 16 bit data is centred around 0x00
  393.     //
  394.     else if (wf.wBitsPerSample == 16) {
  395.         for (x=0; x<w; x++) {
  396.  
  397.         // which byte of audio data belongs at this pixel?
  398.         // Don't make any assumptions about INT size !
  399.         i = *((HPSHORT)lpAudio + muldiv32(x,lLen,w));
  400.  
  401.         if (i > 0) {
  402.             i = y - (int) ((LONG)i * (h/2) / 32768);
  403.             PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  404.         }
  405.         else {
  406.             i = (int) ((LONG)i * (h/2) / 32768);
  407.             PatBlt(hdc, rc.left+x, y, 1, -i, PATCOPY);
  408.         }
  409.         }
  410.     }
  411.     DeleteObject(SelectObject(hdc, hbr));
  412.     } // endif mono
  413.  
  414.     //
  415.     // Draw stereo waveform data
  416.     //
  417.     else if (wf.wf.nChannels == 2) {
  418.  
  419.     //
  420.     // 8 bit data is centred around 0x80
  421.     //
  422.     if (wf.wBitsPerSample == 8) {
  423.  
  424.         // Left channel
  425.         hbr = SelectObject(hdc, CreateSolidBrush(LEFTBRUSH));
  426.         y = rc.top + h/4;
  427.         PatBlt(hdc, rc.left, y, w, 1, PATCOPY);
  428.  
  429.         for (x=0; x<w; x++) {
  430.         b = *((HPBYTE)lpAudio + muldiv32(x,lLen,w) * 2);
  431.  
  432.         if (b > 0x80) {
  433.             i = y - MulDiv(b-0x80,(h/4),128);
  434.             PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  435.         }
  436.         else {
  437.             i = y + MulDiv(0x80-b,(h/4),128);
  438.             PatBlt(hdc, rc.left+x, y, 1, i-y, PATCOPY);
  439.         }
  440.         }
  441.         DeleteObject(SelectObject(hdc, hbr));
  442.  
  443.         // Right channel
  444.         hbr = SelectObject(hdc, CreateSolidBrush(RIGHTBRUSH));
  445.         y = rc.top + h * 3 / 4;
  446.         PatBlt(hdc, rc.left, y, w, 1, PATCOPY);
  447.  
  448.         for (x=0; x<w; x++) {
  449.         b = *((HPBYTE)lpAudio + muldiv32(x,lLen,w) * 2 + 1);
  450.  
  451.         if (b > 0x80) {
  452.             i = y - MulDiv(b-0x80,(h/4),128);
  453.             PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  454.         }
  455.         else {
  456.             i = y + MulDiv(0x80-b,(h/4),128);
  457.             PatBlt(hdc, rc.left+x, y, 1, i-y, PATCOPY);
  458.         }
  459.         }
  460.         DeleteObject(SelectObject(hdc, hbr));
  461.     }
  462.  
  463.     //
  464.     // 16 bit data is centred around 0x00
  465.     //
  466.     else if (wf.wBitsPerSample == 16) {
  467.  
  468.         // Left channel
  469.         hbr = SelectObject(hdc, CreateSolidBrush(LEFTBRUSH));
  470.         y = rc.top + h/4;
  471.         PatBlt(hdc, rc.left, y, w, 1, PATCOPY);
  472.  
  473.         for (x=0; x<w; x++) {
  474.  
  475.         // Don't make any assumptions about INT size !
  476.         i = *((HPSHORT)lpAudio + muldiv32(x,lLen,w) * 2);
  477.         if (i > 0) {
  478.             i = y - (int) ((LONG)i * (h/4) / 32768);
  479.             PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  480.         }
  481.         else {
  482.             i = (int) ((LONG)i * (h/4) / 32768);
  483.             PatBlt(hdc, rc.left+x, y, 1, -i, PATCOPY);
  484.         }
  485.         }
  486.         DeleteObject(SelectObject(hdc, hbr));
  487.  
  488.         // Right channel
  489.         hbr = SelectObject(hdc, CreateSolidBrush(RIGHTBRUSH));
  490.         y = rc.top + h * 3 / 4;
  491.         PatBlt(hdc, rc.left, y, w, 1, PATCOPY);
  492.  
  493.         for (x=0; x<w; x++) {
  494.         // Don't make any assumptions about INT size !
  495.         i = *((HPSHORT)lpAudio + muldiv32(x,lLen,w) * 2 + 1);
  496.         if (i > 0) {
  497.             i = y - (int) ((LONG)i * (h/4) / 32768);
  498.             PatBlt(hdc, rc.left+x, i, 1, y-i, PATCOPY);
  499.         }
  500.         else {
  501.             i = (int) ((LONG)i * (h/4) / 32768);
  502.             PatBlt(hdc, rc.left+x, y, 1, -i, PATCOPY);
  503.         }
  504.         }
  505.         DeleteObject(SelectObject(hdc, hbr));
  506.     }
  507.     } // endif stereo
  508. }
  509.  
  510. /*----------------------------------------------------------------------------*\
  511. |    PaintStuff()                                                               |
  512. |    Do our painting.  Return the height of everything painted so we know how   |
  513. |    much room it took to set our scrollbars.  If fDrawEverything is TRUE,      |
  514. |    we will not stop drawing just because we know we're drawing outside the    |
  515. |    window. This is used to determine how much we would want to draw.          |
  516. \*----------------------------------------------------------------------------*/
  517.  
  518. int PaintStuff(HDC hdc, HWND hwnd, BOOL fDrawEverything)
  519. {
  520.     int         yStreamTop;
  521.     char        szText[BUFSIZE];
  522.     int         iFrameWidth, iLen;
  523.     LONG        lSamp, lCurSamp;
  524.     int         n;
  525.     int         nFrames;
  526.     LPBITMAPINFOHEADER lpbi = NULL;
  527.     LONG        l;
  528.     LONG        lTime;
  529.     LONG        lSize = 0;
  530.     LONG        lAudioStart;
  531.     LONG        lAudioLen;
  532.     RECT        rcFrame, rcC;
  533.     int         i;
  534.     HBRUSH      hbr, hbrOld;
  535.     RECT        rc;
  536.  
  537.     GetClientRect(hwnd, &rcC);
  538.  
  539.     //
  540.     // Look at scrollbars to find current position
  541.     //
  542.     lTime = GetScrollTime(hwnd);
  543.     yStreamTop = -GetScrollPos(hwnd, SB_VERT);
  544.  
  545.     //
  546.     // Walk through all streams and draw something
  547.     //
  548.     for (i=0; i<gcpavi; i++) {
  549.     AVISTREAMINFO   avis;
  550.     LONG            lEnd, lEndTime;
  551.     COLORREF        nCol;
  552.  
  553.     //
  554.     // Remember where this stream begins
  555.     //
  556.     gStreamTop[i] = yStreamTop + GetScrollPos(hwnd, SB_VERT);
  557.  
  558.     //
  559.     // Get some info about this stream
  560.     //
  561.     AVIStreamInfo(gapavi[i], &avis, sizeof(avis));
  562.  
  563.     //
  564.     // Highlight the stream name if we're supposed to
  565.     //
  566.     if (gSelectedStream == MAXNUMSTREAMS+i) {
  567.         hbr = CreateSolidBrush(HIGHLIGHT);
  568.         hbrOld = SelectObject(hdc, hbr);
  569.  
  570.         PatBlt(hdc, 0, yStreamTop, rcC.right, TSPACE * 2, PATCOPY);
  571.         SelectObject(hdc, hbrOld);
  572.         DeleteObject(hbr);
  573.     }
  574.  
  575.     // First we'll print out the stream name
  576.     nCol = SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
  577.     TextOut(hdc, HSPACE, yStreamTop, avis.szName, strlen(avis.szName));
  578.     SetBkColor(hdc, nCol);
  579.  
  580.     // Skip to the next line
  581.     yStreamTop += 2*TSPACE;
  582.  
  583.     if (galSelStart[i] == -1)
  584.     {
  585.         LoadString( ghInstApp, IDS_FORMAT_1, gszBuffer, BUFSIZE );
  586.         iLen = wsprintf(szText,
  587.                 gszBuffer,
  588.                 i,
  589.                 (LPSTR) &avis.fccType,      // compressor FOURCC
  590.                 AVIStreamStart(gapavi[i]),
  591.                 AVIStreamLength(gapavi[i]),
  592.                 (AVIStreamEndTime(gapavi[i]) -
  593.                  AVIStreamStartTime(gapavi[i])) / 1000);
  594.     }
  595.     else
  596.     {
  597.         LoadString( ghInstApp, IDS_FORMAT_2, gszBuffer, BUFSIZE );
  598.         iLen = wsprintf(szText,
  599.                 gszBuffer,
  600.                 i,
  601.                 (LPSTR) &avis.fccType,      // compressor FOURCC
  602.                 AVIStreamStart(gapavi[i]),
  603.                 AVIStreamLength(gapavi[i]),
  604.                 (AVIStreamEndTime(gapavi[i]) -
  605.                  AVIStreamStartTime(gapavi[i])) / 1000,
  606.                 galSelStart[i], galSelStart[i] + galSelLen[i] - 1);
  607.     }
  608.     //
  609.     // Highlight the stream info if we're supposed to
  610.     //
  611.     if (gSelectedStream == i) {
  612.         hbr = CreateSolidBrush(HIGHLIGHT);
  613.         hbrOld = SelectObject(hdc, hbr);
  614.  
  615.         PatBlt(hdc, 0, yStreamTop, rcC.right, TSPACE * 2, PATCOPY);
  616.         SelectObject(hdc, hbrOld);
  617.         DeleteObject(hbr);
  618.     }
  619.  
  620.     nCol = SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
  621.     TextOut(hdc, HSPACE, yStreamTop, szText, iLen);
  622.     SetBkColor(hdc, nCol);
  623.  
  624.     yStreamTop += TSPACE;
  625.     //
  626.     // Draw a VIDEO stream
  627.     //
  628.     if (avis.fccType == streamtypeVIDEO) {
  629.         if (gapgf[i] == NULL)
  630.         continue;
  631.  
  632.         //
  633.         // Which frame belongs at this time?
  634.         //
  635.         lEndTime = AVIStreamEndTime(gapavi[i]);
  636.         if (lTime <= lEndTime)
  637.         lSamp = AVIStreamTimeToSample(gapavi[i], lTime);
  638.         else {      // we've scrolled past the end of this stream
  639.         lEnd = AVIStreamEnd(gapavi[i]);
  640.         lSamp = lEnd + AVIStreamTimeToSample(gapavi[i],
  641.                              lTime - lEndTime);
  642.         }
  643.  
  644.         //
  645.         // how wide is each frame to paint?
  646.         //
  647.         iFrameWidth = (avis.rcFrame.right - avis.rcFrame.left) *
  648.               gwZoom / 4 + HSPACE;
  649.  
  650.         //
  651.         // how many frames can we fit on each half of the screen
  652.         // not counting the one we'll centre?
  653.         //
  654.         nFrames = (rcC.right - iFrameWidth) / (2 * iFrameWidth);
  655.         if (nFrames < 0)
  656.         nFrames = 0;
  657.  
  658.         //
  659.         // Step through all the frames we'll draw
  660.         //
  661.         for (n = -nFrames; n <= nFrames; n++) {
  662.  
  663.         //
  664.         // Each video stream is drawn as a horizontal line of
  665.         // frames, very close together.
  666.         // The first video stream shows a different frame in
  667.         // each square. Thus the scale of time is determined
  668.         // by the first video stream.
  669.         // Every other video stream shows whatever
  670.         // frame belongs at the time corresponding to the mid-
  671.         // point of each square.
  672.         //
  673.         if (i == giFirstVideo) {
  674.  
  675.             //
  676.             // by definition, we know what frame we're drawing..
  677.             // (lSamp-n), (lSamp-(n-1)), ..., (lSamp), ...,
  678.             // (lSamp+n) (lSamp is the one in the centre)
  679.             //
  680.             lCurSamp = lSamp + n;
  681.  
  682.             //
  683.             // what time is it at that frame? This number will
  684.             // be printed underneath the frame
  685.             //
  686.             l = AVIStreamSampleToTime(gapavi[i], lCurSamp);
  687.  
  688.         } else {        // NOT the first video stream
  689.  
  690.             //
  691.             // What time is it at the left hand of the square
  692.             // we'll draw?  That's what frame we use.
  693.             // Does the rounding with MulDiv32 have better properties
  694.             // than muldiv32? It appears to give _slightly_ better performance
  695.  
  696.             l = lTime + MulDiv32(n * (iFrameWidth+HSPACE),
  697.                      gdwMicroSecPerPixel, 1000);
  698.             //if (n<=0) {  // calculate the time for a frame left of centre,
  699.             //             // don't forget the HSPACE offset.
  700.             //    l = lTime - muldiv32(-n * (iFrameWidth + HSPACE),
  701.             //                         gdwMicroSecPerPixel, 1000);
  702.             //
  703.             //}
  704.             //else { // frame is to right of centre.
  705.             //    l = lTime + muldiv32(n * (iFrameWidth +HSPACE),
  706.             //                           gdwMicroSecPerPixel, 1000);
  707.             //
  708.             //}
  709.  
  710.             //
  711.             // What frame belongs to that time?
  712.             //
  713.             lCurSamp = AVIStreamTimeToSample(gapavi[i], l);
  714.  
  715.             //
  716.             // Use the exact time of that frame when printing
  717.             //
  718.             l = AVIStreamSampleToTime(gapavi[i], lCurSamp);
  719.         }
  720.  
  721.         // !!!
  722.         // Could actually return an LPBI for invalid frames
  723.         // so we better force it to NULL.
  724.         //
  725.         if (gapgf[i] && lCurSamp >= AVIStreamStart(gapavi[i])) // &&
  726.             //lCurSamp <= AVIStreamEnd(gapavi[i]))
  727.             lpbi = AVIStreamGetFrame(gapgf[i], lCurSamp);
  728.         else
  729.             lpbi = NULL;
  730.  
  731.         //
  732.         // Figure out where to draw this frame
  733.         //
  734.         rcFrame.left   = rcC.right / 2 -
  735.                  ((avis.rcFrame.right - avis.rcFrame.left) * gwZoom / 4)
  736.                  / 2 + (n * iFrameWidth);
  737.         rcFrame.top    = yStreamTop + TSPACE;
  738.         rcFrame.right  = rcFrame.left +
  739.                  (avis.rcFrame.right - avis.rcFrame.left) * gwZoom / 4;
  740.         rcFrame.bottom = rcFrame.top +
  741.                  (avis.rcFrame.bottom - avis.rcFrame.top) * gwZoom / 4;
  742.  
  743.         //
  744.         // If this frame is selected, highlight it
  745.         //
  746.         if (lCurSamp >= galSelStart[i] &&
  747.             lCurSamp < galSelStart[i] +
  748.             galSelLen[i]) {
  749.             hbr = CreateSolidBrush(HIGHLIGHT);
  750.         }
  751.         else { //not highlighted - but need to clear area around frame
  752.                // of selection
  753.             hbr = CreateSolidBrush(GetBkColor(hdc));
  754.         }
  755.  
  756.         FrameVideo(hdc, &rcFrame, hbr);
  757.         DeleteObject (hbr);
  758.  
  759.         //
  760.         // draw a border around the centre frame.
  761.         //
  762.         if (n == 0) {
  763.             hbr = CreateSolidBrush(RGB(255,0,0));
  764.             InflateRect(&rcFrame, 1, 1);
  765.             FrameRect(hdc, &rcFrame, hbr);
  766.             InflateRect(&rcFrame, -1, -1);
  767.             DeleteObject (hbr);
  768.         }
  769.  
  770.         //
  771.         // Now draw the video frame in the computed rectangle
  772.         //
  773.         PaintVideo(hdc, rcFrame, i, lpbi, lCurSamp, l);
  774.         }
  775.  
  776.         //
  777.         // Print a description of this stream
  778.         //
  779.         if (lpbi)
  780.         AVIStreamSampleSize(gapavi[i], lSamp, &lSize);
  781.  
  782.         //
  783.         // Move down to where we can draw the next stream
  784.         //
  785.         yStreamTop += TSPACE +
  786.               (rcFrame.bottom - rcFrame.top) +
  787.               TSPACE;
  788.     }
  789.  
  790.     //
  791.     // Draw an AUDIO stream
  792.     //
  793.     else if (avis.fccType == streamtypeAUDIO) {
  794.  
  795.         //
  796.         // Figure out which samples are visible
  797.         //
  798.         lAudioStart = lTime - muldiv32(rcC.right / 2,
  799.                        gdwMicroSecPerPixel, 1000);
  800.         lAudioLen = 2 * (lTime - lAudioStart);
  801.  
  802.         // clear the selection area
  803.  
  804.         hbr = CreateSolidBrush(GetBkColor(hdc));
  805.         hbrOld = SelectObject(hdc, hbr);
  806.         PatBlt(hdc,
  807.         0,
  808.         yStreamTop + TSPACE - SELECTVSPACE,
  809.         rcC.right,
  810.         SELECTVSPACE,
  811.         PATCOPY);
  812.         PatBlt(hdc,
  813.         0,
  814.         yStreamTop + TSPACE + (AUDIOVSPACE * gwZoom/4),
  815.         rcC.right,
  816.         SELECTVSPACE,
  817.         PATCOPY);
  818.         SelectObject(hdc, hbrOld);
  819.         DeleteObject (hbr);
  820.  
  821.         //
  822.         // We have a selection... Highlight it
  823.         //
  824.         if (galSelStart[i] != -1) {
  825.         LONG lSelSt, lSelLen;
  826.  
  827.         //
  828.         // What time is our selection?
  829.         //
  830.         lSelSt = AVIStreamSampleToTime(gapavi[i], galSelStart[i]);
  831.         lSelLen = AVIStreamSampleToTime(gapavi[i], galSelLen[i]);
  832.  
  833.         //
  834.         // At what pixels is our selection?
  835.         //
  836.         if (lSelSt < lTime) { //selecting to the left of the current position
  837.             rc.left = rcC.right /2 - (int)
  838.             muldiv32((lTime - lSelSt) , 1000, gdwMicroSecPerPixel);
  839.         }
  840.         else {
  841.             rc.left = rcC.right / 2 + (int)
  842.             muldiv32(lSelSt - lTime, 1000, gdwMicroSecPerPixel);
  843.         }
  844.         rc.right = rc.left + (int)
  845.             muldiv32(lSelLen, 1000, gdwMicroSecPerPixel);
  846.  
  847.         // Selection starts way past left side of screen
  848.         if (lSelSt < lAudioStart)
  849.             rc.left = 0;
  850.         // Selection is off, screen left
  851.         if (lSelSt + lSelLen < lAudioStart)
  852.             rc.right = - SELECTVSPACE;
  853.  
  854.         // Selection is off, screen right
  855.         if (lSelSt > lAudioStart + lAudioLen)
  856.             rc.left = rcC.right + SELECTVSPACE;
  857.  
  858.         // Selection ends past the right side of the screen
  859.         if (lSelSt + lSelLen > lAudioStart + lAudioLen)
  860.             rc.right = rcC.right;
  861.         if (rc.right == rc.left)        // draw SOMEthing.
  862.             rc.right++;
  863.         rc.top = yStreamTop + TSPACE;
  864.         rc.bottom = rc.top + AUDIOVSPACE * gwZoom / 4;
  865.  
  866.         //
  867.         // Draw some indication
  868.         //
  869.         hbr = CreateSolidBrush(HIGHLIGHT);
  870.         hbrOld = SelectObject(hdc, hbr);
  871.         PatBlt(hdc,
  872.                rc.left - SELECTVSPACE,
  873.                rc.top - SELECTVSPACE,
  874.                rc.right - rc.left + 2 * SELECTVSPACE,
  875.                SELECTVSPACE,
  876.                PATCOPY);
  877.         PatBlt(hdc,
  878.                rc.left - SELECTVSPACE,
  879.                rc.bottom,
  880.                rc.right - rc.left + 2 * SELECTVSPACE,
  881.                SELECTVSPACE,
  882.                PATCOPY);
  883.         SelectObject(hdc, hbrOld);
  884.         DeleteObject (hbr);
  885.         }
  886.  
  887.         //
  888.         // Make the rectangle to draw audio into
  889.         //
  890.         rc.left = rcC.left;
  891.         rc.right = rcC.right;
  892.         rc.top = yStreamTop + TSPACE;
  893.         rc.bottom = rc.top + AUDIOVSPACE * gwZoom / 4;
  894.  
  895.         //
  896.         // Actually paint the audio
  897.         //
  898.         PaintAudio(hdc, &rc, gapavi[i], lAudioStart, lAudioLen);
  899.  
  900.         //
  901.         // Print the time at the centre of the audio stream
  902.         //
  903.         iLen = wsprintf(szText, "%ld.%03lds", lTime/1000, lTime%1000);
  904.         rc.left = (rc.right - rc.left) / 2 - 5 * HSPACE;
  905.         rc.right = (rc.right - rc.left) / 2;
  906.         rc.top = rc.bottom + HSPACE; rc.bottom = rc.top + VSPACE;
  907.         nCol = SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
  908.         ExtTextOut(hdc, rc.left, rc.top, 0, &rc, szText, iLen, NULL);
  909.         SetBkColor(hdc, nCol);
  910.  
  911.         //
  912.         // Move down to where we can draw the next stream
  913.         //
  914.         yStreamTop += TSPACE + AUDIOVSPACE * gwZoom / 4;
  915.  
  916.     }
  917.  
  918.     yStreamTop += TSPACE + TSPACE;
  919.  
  920.     //
  921.     // Give up once we're painting below the bottom of the window
  922.     //
  923.     if (!fDrawEverything && yStreamTop >= rcC.bottom)
  924.         break;
  925.     }
  926.  
  927.     // The bottom of all the streams;
  928.     gStreamTop[gcpavi] = yStreamTop + GetScrollPos(hwnd, SB_VERT);
  929.  
  930.     //
  931.     // How many lines did we draw?
  932.     //
  933.     return yStreamTop + GetScrollPos(hwnd, SB_VERT);
  934. }
  935.  
  936. /*----------------------------------------------------------------------------*\
  937. |    FixScrollBars()                                                            |
  938. |    When we load a file or zoom changes, we re-set the scrollbars              |
  939. \*----------------------------------------------------------------------------*/
  940.  
  941. void FixScrollbars(HWND hwnd)
  942. {
  943.     int                 nHeight = 0;
  944.     RECT                rc;
  945.     HDC                 hdc;
  946.  
  947.     //
  948.     // Determine how tall our window needs to be to display everything.
  949.     //
  950.     hdc = GetDC(NULL);
  951.     ExcludeClipRect(hdc, 0, 0, 32767, 32767);   // don't actually draw
  952.     nHeight = PaintStuff(hdc, hwnd, TRUE);
  953.     ReleaseDC(NULL, hdc);
  954.  
  955.     //
  956.     // Set vertical scrollbar for scrolling the visible area
  957.     //
  958.     GetClientRect(hwnd, &rc);
  959.     nVertHeight = nHeight;      // total height in pixels of entire display
  960.  
  961.     //
  962.     // We won't fit in the window... need scrollbars
  963.     //
  964.     if (nHeight > rc.bottom)
  965.     {
  966.     nVertSBLen = nHeight - rc.bottom;
  967.     SetScrollRange(hwnd, SB_VERT, 0, nVertSBLen, TRUE);
  968.     SetScrollPos(hwnd, SB_VERT, 0, TRUE);
  969.  
  970.     //
  971.     // We will fit in the window!  No scrollbars necessary
  972.     //
  973.     }
  974.     else
  975.     {
  976.     nVertSBLen = 0;
  977.     SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
  978.     }
  979.     return;
  980. }
  981.  
  982.  
  983.  
  984. /*----------------------------------------------------------------------------*\
  985. |    InitStreams()                                                              |
  986. |    Initialize the streams of a loaded file -- the compression options, the    |
  987. |    DrawDIB handles, and the scroll bars.                                      |
  988. |    !!! This clears the compression options right now every time it's called!  |
  989. \*----------------------------------------------------------------------------*/
  990.  
  991. void InitStreams(HWND hwnd)
  992. {
  993.     AVISTREAMINFO     avis;
  994.     LONG        lTemp;
  995.     int         i;
  996.  
  997.     //
  998.     // Start with bogus times
  999.     //
  1000.     timeStart = 0x7FFFFFFF;
  1001.     timeEnd   = 0;
  1002.  
  1003.     //
  1004.     // Walk through and init all streams loaded
  1005.     //
  1006.     for (i = 0; i < gcpavi; i++) {
  1007.  
  1008.     AVIStreamInfo(gapavi[i], &avis, sizeof(avis));
  1009.  
  1010.     //
  1011.     // Save and SaveOptions code takes a pointer to our compression opts
  1012.     //
  1013.     galpAVIOptions[i] = &gaAVIOptions[i];
  1014.  
  1015.     //
  1016.     // clear options structure to zeroes
  1017.     //
  1018.     _fmemset(galpAVIOptions[i], 0, sizeof(AVICOMPRESSOPTIONS));
  1019.  
  1020.     //
  1021.     // Initialize the compression options to some default stuff
  1022.     // !!! Pick something better
  1023.     //
  1024.     galpAVIOptions[i]->fccType = avis.fccType;
  1025.  
  1026.     switch(avis.fccType) {
  1027.  
  1028.         case streamtypeVIDEO:
  1029.         galpAVIOptions[i]->dwFlags = AVICOMPRESSF_VALID |
  1030.                 AVICOMPRESSF_KEYFRAMES | AVICOMPRESSF_DATARATE;
  1031.         galpAVIOptions[i]->fccHandler = 0;
  1032.         galpAVIOptions[i]->dwQuality = (DWORD)ICQUALITY_DEFAULT;
  1033.         galpAVIOptions[i]->dwKeyFrameEvery = (DWORD)-1; // Default
  1034.         galpAVIOptions[i]->dwBytesPerSecond = 0;
  1035.         galpAVIOptions[i]->dwInterleaveEvery = 1;
  1036.         break;
  1037.  
  1038.         case streamtypeAUDIO:
  1039.         galpAVIOptions[i]->dwFlags |= AVICOMPRESSF_VALID;
  1040.         galpAVIOptions[i]->dwInterleaveEvery = 1;
  1041.         AVIStreamReadFormat(gapavi[i],
  1042.                     AVIStreamStart(gapavi[i]),
  1043.                     NULL,
  1044.                     &lTemp);
  1045.         galpAVIOptions[i]->cbFormat = lTemp;
  1046.         if (lTemp)
  1047.             galpAVIOptions[i]->lpFormat = GlobalAllocPtr(GHND, lTemp);
  1048.         // Use current format as default format
  1049.         if (galpAVIOptions[i]->lpFormat)
  1050.             AVIStreamReadFormat(gapavi[i],
  1051.                     AVIStreamStart(gapavi[i]),
  1052.                     galpAVIOptions[i]->lpFormat,
  1053.                     &lTemp);
  1054.         break;
  1055.  
  1056.         default:
  1057.         break;
  1058.     }
  1059.  
  1060.     //
  1061.     // We're finding the earliest and latest start and end points for
  1062.     // our scrollbar.
  1063.     //
  1064.     timeStart = min(timeStart, AVIStreamStartTime(gapavi[i]));
  1065.     timeEnd   = max(timeEnd, AVIStreamEndTime(gapavi[i]));
  1066.  
  1067.     //
  1068.     // Initialize video streams for getting decompressed frames to display
  1069.     //
  1070.     if (avis.fccType == streamtypeVIDEO) {
  1071.  
  1072.         gapgf[i] = AVIStreamGetFrameOpen(gapavi[i], NULL);
  1073.  
  1074.         if (gapgf[i] == NULL)
  1075.         continue;
  1076.  
  1077.         ghdd[i] = DrawDibOpen();
  1078.         // !!! DrawDibBegin?
  1079.  
  1080.         if (!gfVideoFound) {
  1081.         DWORD        dw;
  1082.  
  1083.         //
  1084.         // Remember the first video stream --- treat it specially
  1085.         //
  1086.         giFirstVideo = i;
  1087.  
  1088.         //
  1089.         // Set the horizontal scrollbar scale to show every frame
  1090.         // of the first video stream exactly once
  1091.         //
  1092.         dw = (avis.rcFrame.right - avis.rcFrame.left) * gwZoom / 4 + HSPACE;
  1093.  
  1094.         gdwMicroSecPerPixel = muldiv32(1000000,
  1095.                            avis.dwScale,
  1096.                            dw * avis.dwRate);
  1097.         // Move one frame on the top video screen for each HSCROLL
  1098.         timehscroll = muldiv32(1000, avis.dwScale, avis.dwRate);
  1099.         }
  1100.  
  1101.     } else if (avis.fccType == streamtypeAUDIO) {
  1102.  
  1103.         // These aren't used and better be NULL!
  1104.         gapgf[i] = ghdd[i] = NULL;
  1105.  
  1106.         //
  1107.         // If there are no video streams, we base everything on this
  1108.         // audio stream.
  1109.         //
  1110.         if (!gfAudioFound && !gfVideoFound) {
  1111.  
  1112.         // Show one sample per pixel
  1113.         gdwMicroSecPerPixel = muldiv32(1000000,
  1114.                            avis.dwScale,
  1115.                            avis.dwRate);
  1116.         // Move one sample per HSCROLL
  1117.         // Move at least enough to show movement
  1118.         timehscroll = muldiv32(1000, avis.dwScale, avis.dwRate);
  1119.         }
  1120.  
  1121.         //
  1122.         // Remember the first audio stream --- treat it specially
  1123.         //
  1124.         if (!gfAudioFound)
  1125.         giFirstAudio = i;
  1126.  
  1127.     }
  1128.  
  1129.     }
  1130.  
  1131.     timeLength = timeEnd - timeStart;
  1132.  
  1133.     if (timeLength == 0)
  1134.     timeLength = 1;
  1135.  
  1136.     // Make sure HSCROLL scrolls enough to be noticeable.
  1137.     timehscroll = max(timehscroll, timeLength / SCROLLRANGE + 2);
  1138.  
  1139.     SetScrollRange(hwnd, SB_HORZ, 0, SCROLLRANGE, TRUE);
  1140.     SetScrollTime(hwnd, timeStart);
  1141.  
  1142.     FixScrollbars(hwnd);
  1143. }
  1144.  
  1145.  
  1146. /*----------------------------------------------------------------------------*\
  1147. |    FixWindowTitle()                                                           |
  1148. |    Update the window title to reflect what's loaded                           |
  1149. \*----------------------------------------------------------------------------*/
  1150.  
  1151. void FixWindowTitle(HWND hwnd)
  1152. {
  1153.     char szTitle[2*BUFSIZE];
  1154.  
  1155.     LoadString( ghInstApp, IDS_APPNAME, gszBuffer, BUFSIZE );
  1156.  
  1157.     wsprintf(szTitle, "%s %s", (LPSTR)gszBuffer, (LPSTR)gszFileName);
  1158.  
  1159.     SetWindowText( hwnd, szTitle );
  1160.  
  1161.     InvalidateRect(hwnd, NULL, TRUE);
  1162. }
  1163.  
  1164. /*----------------------------------------------------------------------------*\
  1165. |    FreeDrawStuff()                                                            |
  1166. |    Free up the resources associated with DrawDIB.                             |
  1167. \*----------------------------------------------------------------------------*/
  1168.  
  1169. void FreeDrawStuff(HWND hwnd)
  1170. {
  1171.     int i;
  1172.  
  1173.     // Make sure we're not playing!
  1174.     aviaudioStop();
  1175.  
  1176.     for (i = 0; i < gcpavi; i++) {
  1177.     if (gapgf[i]) {
  1178.         AVIStreamGetFrameClose(gapgf[i]);
  1179.         gapgf[i] = NULL;
  1180.     }
  1181.     if (ghdd[i]) {
  1182.         DrawDibClose(ghdd[i]);
  1183.         ghdd[i] = 0;
  1184.     }
  1185.     }
  1186.     SetScrollRange(hwnd, SB_HORZ, 0, 0, TRUE);
  1187.     giFirstVideo = giFirstAudio = -1;
  1188. }
  1189.  
  1190.  
  1191. /*----------------------------------------------------------------------------*\
  1192. |    NukeAVIStream()                                                            |
  1193. |    Get rid of a stream in our array and compact it.                           |
  1194. \*----------------------------------------------------------------------------*/
  1195.  
  1196. void NukeAVIStream(int i)
  1197. {
  1198.     int j;
  1199.  
  1200.     //
  1201.     // Make sure it's a real stream number
  1202.     //
  1203.     if (i < 0 || i >=gcpavi)
  1204.     return;
  1205.  
  1206.     //
  1207.     // Free all the resources associated with this stream
  1208.     //
  1209.     AVIStreamRelease(gapavi[i]);
  1210.     if (galpAVIOptions[i]->lpFormat) {
  1211.     GlobalFreePtr(galpAVIOptions[i]->lpFormat);
  1212.     }
  1213.     if (gapgf[i]) {
  1214.     AVIStreamGetFrameClose(gapgf[i]);
  1215.     gapgf[i] = NULL;
  1216.     }
  1217.     if (ghdd[i]) {
  1218.     DrawDibClose(ghdd[i]);
  1219.     ghdd[i] = 0;
  1220.     }
  1221.  
  1222.     //
  1223.     // Compact the arrays of junk
  1224.     //
  1225.     for (j = i; j < gcpavi - 1; j++) {
  1226.     gapavi[j] = gapavi[j+1];
  1227.     galpAVIOptions[j] = galpAVIOptions[j+1];
  1228.     gapgf[j] = gapgf[j+1];
  1229.     ghdd[j] = ghdd[j+1];
  1230.     }
  1231.  
  1232.     gcpavi--;
  1233. }
  1234.  
  1235. /*----------------------------------------------------------------------------*\
  1236. |    FreeAVI()                                                                  |
  1237. |    Free the resources associated with an open file.                           |
  1238. \*----------------------------------------------------------------------------*/
  1239.  
  1240. void FreeAvi(HWND hwnd)
  1241. {
  1242.     int i;
  1243.  
  1244.     FreeDrawStuff(hwnd);
  1245.  
  1246.     AVISaveOptionsFree(gcpavi, galpAVIOptions);
  1247.  
  1248.     for (i = 0; i < gcpavi; i++) {
  1249.     AVIStreamRelease(gapavi[i]);
  1250.     }
  1251.  
  1252.     // Good a place as any to make sure audio data gets freed
  1253.     if (lpAudio)
  1254.     GlobalFreePtr(lpAudio);
  1255.     lpAudio = NULL;
  1256.  
  1257.     gcpavi = 0;
  1258. }
  1259.  
  1260. /*----------------------------------------------------------------------------*\
  1261. |    InsertAVIFile()                                                            |
  1262. |    Put a new AVI file into our internal structures.                           |
  1263. \*----------------------------------------------------------------------------*/
  1264.  
  1265. void InsertAVIFile(PAVIFILE pfile, HWND hwnd, LPSTR lpszFile)
  1266. {
  1267.     int         i;
  1268.     PAVISTREAM  pavi;
  1269.  
  1270.     for (i = gcpavi; i <= MAXNUMSTREAMS; i++) {
  1271.     if (AVIFileGetStream(pfile, &pavi, 0L, i - gcpavi) != AVIERR_OK)
  1272.         break;
  1273.     if (i == MAXNUMSTREAMS)
  1274.     {
  1275.         AVIStreamRelease(pavi);
  1276.         LoadString( ghInstApp, IDS_MAXSTREAMS, gszBuffer, BUFSIZE );
  1277.         ErrMsg(gszBuffer);
  1278.         break;
  1279.     }
  1280.     if (CreateEditableStream(&gapavi[i], pavi) != AVIERR_OK) {
  1281.         AVIStreamRelease(pavi);
  1282.         break;
  1283.     }
  1284.     AVIStreamRelease(pavi);
  1285.     galSelStart[i] = galSelLen[i] = -1;
  1286.     }
  1287.  
  1288.     AVIFileRelease(pfile);
  1289.  
  1290.     if (gcpavi == i && i != MAXNUMSTREAMS)
  1291.     {
  1292.  
  1293.     LoadString( ghInstApp, IDS_NOOPEN, gszBuffer, BUFSIZE );
  1294.  
  1295.     ErrMsg(gszBuffer, lpszFile);
  1296.     return;
  1297.     }
  1298.  
  1299.     FreeDrawStuff(hwnd);
  1300.     gcpavi = i;
  1301.     InitStreams(hwnd);
  1302.     FixWindowTitle(hwnd);
  1303. }
  1304.  
  1305.  
  1306. /*----------------------------------------------------------------------------*\
  1307. |    InitAVI()                                                                  |
  1308. |    Open up a file through the AVIFile handlers.                               |
  1309. \*----------------------------------------------------------------------------*/
  1310.  
  1311.  
  1312. void InitAvi(HWND hwnd, LPSTR szFile, int nMenu)
  1313. {
  1314.     HRESULT     hr;
  1315.     PAVIFILE    pfile;
  1316.  
  1317.     hr = AVIFileOpen(&pfile, szFile, 0, 0L);
  1318.  
  1319.     if (hr != 0)
  1320.     {
  1321.     LoadString( ghInstApp, IDS_NOOPEN, gszBuffer, BUFSIZE );
  1322.     ErrMsg(gszBuffer, szFile);
  1323.     return;
  1324.     }
  1325.  
  1326.     //
  1327.     // If we're opening something new, close other open files, otherwise
  1328.     // just close the draw stuff so we'll merge streams with the new file
  1329.     //
  1330.     if (nMenu == MENU_OPEN)
  1331.     FreeAvi(hwnd);
  1332.  
  1333.     InsertAVIFile(pfile, hwnd, szFile);
  1334. }
  1335.  
  1336. /*----------------------------------------------------------------------------*\
  1337. |    DropAvi()                                                                  |
  1338. |    Allow a drag/drop on AVIEdit.                                              |
  1339. \*----------------------------------------------------------------------------*/
  1340. void DropAvi(HWND hwnd, HDROP hDrop)
  1341. {
  1342.     char        szPath[BUFSIZE];
  1343.     UINT        nDropped, n;
  1344.     PAVIFILE    pfile;
  1345.     HRESULT     hr;
  1346.  
  1347.     // Get number of files dropped
  1348.     nDropped = DragQueryFile(hDrop,0xFFFF,NULL,0);
  1349.  
  1350.     if (nDropped) {
  1351.     SetActiveWindow(hwnd);
  1352.  
  1353.     // If we wanted to, we could simulate a click at the position
  1354.     // the drop took place....
  1355.  
  1356.     for (n = 0; n < nDropped; n++) {
  1357.         // Get the file that was dropped....
  1358.         DragQueryFile(hDrop, n, szPath, BUFSIZE);
  1359.  
  1360.         hr = AVIFileOpen(&pfile, szPath, 0, 0L);
  1361.  
  1362.         if (hr == 0) {
  1363.         // ... and paste it in.
  1364.         editPaste(hwnd, pfile);
  1365.         }
  1366.     }
  1367.     }
  1368.     DragFinish(hDrop);     /* Delete structure alocated */
  1369. }
  1370.  
  1371. /*----------------------------------------------------------------------------*\
  1372. |   AppInit( hInst, hPrev)                                                     |
  1373. |                                                                              |
  1374. |   Description:                                                               |
  1375. |       This is called when the application is first loaded into               |
  1376. |       memory.  It performs all initialization that doesn't need to be done   |
  1377. |       once per instance.                                                     |
  1378. |                                                                              |
  1379. |   Arguments:                                                                 |
  1380. |       hInstance       instance handle of current instance                    |
  1381. |       hPrev           instance handle of previous instance                   |
  1382. |                                                                              |
  1383. |   Returns:                                                                   |
  1384. |       TRUE if successful, FALSE if not                                       |
  1385. |                                                                              |
  1386. \*----------------------------------------------------------------------------*/
  1387.  
  1388. BOOL AppInit(HINSTANCE hInst, HINSTANCE hPrev, int sw, LPSTR szCmdLine)
  1389. {
  1390.     WNDCLASS cls;
  1391.     WORD        wVer;
  1392.  
  1393.     /* first let's make sure we are running on 1.1 */
  1394.     wVer = HIWORD(VideoForWindowsVersion());
  1395.     if (wVer < 0x010a)
  1396.     {
  1397.     char szTitle[BUFSIZE];
  1398.     /* oops, we are too old, blow out of here */
  1399.     MessageBeep(MB_ICONHAND);
  1400.     LoadString( ghInstApp, IDS_APPERR, szTitle, BUFSIZE );
  1401.     LoadString( ghInstApp, IDS_OLDVFW, gszBuffer, BUFSIZE );
  1402.  
  1403.     MessageBox(NULL, gszBuffer, szTitle, MB_OK|MB_ICONSTOP);
  1404.     return FALSE;
  1405.     }
  1406.  
  1407.     //
  1408.     // Save instance handle for DialogBoxs
  1409.     //
  1410.     ghInstApp = hInst;
  1411.  
  1412.     ghAccel = LoadAccelerators(hInst, MAKEINTATOM(ID_APP));
  1413.  
  1414.     //
  1415.     // Did we get passed a filename on the command line? We'll open it at create
  1416.     // time.
  1417.     //
  1418.     if (szCmdLine && szCmdLine[0])
  1419.     lstrcpy(gszFileName, szCmdLine);
  1420.     else
  1421.     lstrcpy( gszFileName, "" );
  1422.  
  1423.     lstrcpy( gszSaveFileName, "" );
  1424.  
  1425.     if (!hPrev) {
  1426.     /*
  1427.      *  Register a class for the main application window
  1428.      */
  1429.     cls.hCursor        = LoadCursor(NULL,IDC_ARROW);
  1430.     cls.hIcon          = LoadIcon(hInst,MAKEINTATOM(ID_APP));
  1431.     cls.lpszMenuName   = MAKEINTATOM(ID_APP);
  1432.     cls.lpszClassName  = MAKEINTATOM(ID_APP);
  1433.     cls.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
  1434.     cls.hInstance      = hInst;
  1435.     cls.style          = CS_BYTEALIGNCLIENT | CS_VREDRAW | CS_HREDRAW |
  1436.                  CS_DBLCLKS;
  1437.     cls.lpfnWndProc    = (WNDPROC)AppWndProc;
  1438.     cls.cbWndExtra     = 0;
  1439.     cls.cbClsExtra     = 0;
  1440.  
  1441.     if (!RegisterClass(&cls))
  1442.         return FALSE;
  1443.     }
  1444.  
  1445.     //
  1446.     // Must be called before using any of the AVIFile routines
  1447.     //
  1448.     AVIFileInit();
  1449.  
  1450.     LoadString( ghInstApp, IDS_APPNAME, gszBuffer, BUFSIZE );
  1451.     //
  1452.     // Create our main application window
  1453.     //
  1454.     ghwndApp = CreateWindow (
  1455.                  MAKEINTATOM(ID_APP),    // Class name
  1456.                  gszBuffer,             // Caption
  1457.                  WS_OVERLAPPEDWINDOW,    // Style bits
  1458.                  CW_USEDEFAULT, 0,       // Position
  1459.                  320,300,                // Size
  1460.                  (HWND)NULL,             // Parent window (no parent)
  1461.                  (HMENU)NULL,            // use class menu
  1462.                  hInst,                  // handle to window instance
  1463.                  (LPSTR)NULL             // no params to pass on
  1464.                  );
  1465.     ShowWindow(ghwndApp,sw);
  1466.  
  1467.     return TRUE;
  1468. }
  1469.  
  1470. /*----------------------------------------------------------------------------*\
  1471. |   WinMain( hInst, hPrev, lpszCmdLine, cmdShow )                              |
  1472. |                                                                              |
  1473. |   Description:                                                               |
  1474. |       The main procedure for the App.  After initializing, it just goes      |
  1475. |       into a message-processing loop until it gets a WM_QUIT message         |
  1476. |       (meaning the app was closed). If the preview is playing it adjusts     |
  1477. |       the scrollbar appropriately.                                           |
  1478. |                                                                              |
  1479. |   Arguments:                                                                 |
  1480. |       hInst           instance handle of this instance of the app            |
  1481. |       hPrev           instance handle of previous instance, NULL if first    |
  1482. |       szCmdLine       ->null-terminated command line                         |
  1483. |       cmdShow         specifies how the window is initially displayed        |
  1484. |                                                                              |
  1485. |   Returns:                                                                   |
  1486. |       The exit code as specified in the WM_QUIT message.                     |
  1487. |                                                                              |
  1488. \*----------------------------------------------------------------------------*/
  1489. int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
  1490. {
  1491.     MSG     msg;
  1492.  
  1493.     //
  1494.     // Call our initialization procedure
  1495.     //
  1496.     if (!AppInit(hInst, hPrev, sw, szCmdLine))
  1497.     return FALSE;
  1498.  
  1499.     /*
  1500.      * Polling messages from event queue
  1501.      */
  1502.     for (;;)
  1503.     {
  1504.     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  1505.     {
  1506.         if (msg.message == WM_QUIT)
  1507.         return msg.wParam;
  1508.  
  1509.         if (TranslateAccelerator(ghwndApp, ghAccel, &msg))
  1510.         continue;
  1511.  
  1512.         TranslateMessage(&msg);
  1513.         DispatchMessage(&msg);
  1514.     }
  1515.  
  1516.     //
  1517.     // If we have no messages to dispatch, we do our background task...
  1518.     // If we're playing a file, we set the scroll bar to show the video
  1519.     // frames corresponding with the current playing audio sample
  1520.     //
  1521.     if (gfPlaying) {
  1522.         LONG    l;
  1523.  
  1524.         //
  1525.         // Use the audio clock to tell how long we've been playing.  To
  1526.         // maintain sync, it's important we use this clock.
  1527.         //
  1528.         l = aviaudioTime();         // returns -1 if no audio playing
  1529.  
  1530.         //
  1531.         // If we can't use the audio clock to tell us how long we've been
  1532.         // playing, calculate it ourself
  1533.         //
  1534.         if (l == -1)
  1535.         l = timeGetTime() - glPlayStartTime + glPlayStartPos;
  1536.  
  1537.         if (l != (LONG)GetScrollTime(ghwndApp)) {
  1538.         if (l < timeStart)      // make sure number isn't out of bounds
  1539.             l = timeStart;
  1540.         if (l > timeEnd)        // looks like we're all done!
  1541.             FORWARD_WM_COMMAND(ghwndApp, MENU_STOP, NULL, 0, SendMessage);
  1542.  
  1543.         SetScrollTime(ghwndApp, l);
  1544.         InvalidateRect(ghwndApp, NULL, FALSE);
  1545.         UpdateWindow(ghwndApp);
  1546.  
  1547.         continue;
  1548.         }
  1549.     }
  1550.  
  1551.     WaitMessage();
  1552.     }
  1553.  
  1554.     /* NOT REACHED */
  1555. }
  1556.  
  1557. /*----------------------------------------------------------------------------*\
  1558. |    SelectStream()                                                             |
  1559. |                                                                               |
  1560. |    Selects a portion of a stream i:                                           |
  1561. |                                                                               |
  1562. |    i == -1 means clear all selections                                         |
  1563. |    start == -1 means clear that individual stream's selection                 |
  1564. |    fAdd == TRUE means extend that stream's selection to include the new       |
  1565. |            range (will otherwise just replace the selection)                  |
  1566. |    fAll == TRUE means select this range in every stream, not just i           |
  1567. \*----------------------------------------------------------------------------*/
  1568.  
  1569. void SelectStream(HWND hwnd, int i, LONG start, LONG length, BOOL fAdd, BOOL fAll)
  1570. {
  1571.     int n, j;
  1572.     LONG mystart, mylength;
  1573.     RECT rc;
  1574.  
  1575.     //
  1576.     // Clear all selections
  1577.     //
  1578.     if (i == -1) {
  1579.     for (n = 0; n < gcpavi; n++)
  1580.         galSelStart[n] = galSelLen[n] = -1;
  1581.  
  1582.     } else if (i >= 0 && i < gcpavi) {  // valid stream number
  1583.  
  1584.     //
  1585.     // We've been told to clear this selection
  1586.     //
  1587.     if (start == -1 || length == -1)
  1588.         galSelStart[i] = galSelLen[i] = -1;
  1589.  
  1590.     //
  1591.     // Is this a valid selection range?
  1592.     //
  1593.     if (start >=AVIStreamStart(gapavi[i]) &&
  1594.         start < AVIStreamEnd(gapavi[i]) &&
  1595.         length >= 1) {
  1596.  
  1597.         //
  1598.         // Do we select the same range in every stream or just one?
  1599.         //
  1600.         for (j = (fAll ? 0 : i); j < (fAll ? gcpavi : i+1); j++) {
  1601.  
  1602.         //
  1603.         // Translate for each stream the equivalent region to select
  1604.         //
  1605.         if (j == i) {
  1606.             mystart = start; mylength = length;
  1607.         } else {
  1608.             mystart = AVIStreamSampleToSample(gapavi[j],
  1609.                               gapavi[i], start);
  1610.             mylength = max(1, AVIStreamSampleToSample(gapavi[j],
  1611.                                   gapavi[i], length));    // at least 1
  1612.             // !!! Better invalidate this entire stream since we're not
  1613.             // sure what part needs repainting.
  1614.             GetClientRect(hwnd, &rc);
  1615.             rc.top = gStreamTop[j] - GetScrollPos(hwnd, SB_VERT);
  1616.             rc.bottom = gStreamTop[j+1] - GetScrollPos(hwnd, SB_VERT);
  1617.             InvalidateRect(hwnd, &rc, FALSE);
  1618.         }
  1619.  
  1620.         //
  1621.         // Verify we got good selection values
  1622.         //
  1623.         if (mystart < AVIStreamStart(gapavi[j])) {
  1624.             mylength -= AVIStreamStart(gapavi[j]) - mystart;
  1625.             mystart = AVIStreamStart(gapavi[j]);
  1626.         }
  1627.         if (mystart + mylength > AVIStreamEnd(gapavi[j]))
  1628.             mylength -= mystart + mylength - AVIStreamEnd(gapavi[j]);
  1629.         if (mylength <= 0)
  1630.             mystart = -1;
  1631.         if (mystart == -1)      // nothing to select in this stream
  1632.             continue;
  1633.  
  1634.         //
  1635.         // Reset selection to new values
  1636.         //
  1637.         if (!fAdd || galSelStart[j] == -1) {
  1638.             galSelStart[j] = mystart;
  1639.             galSelLen[j] = mylength;
  1640.  
  1641.             //
  1642.             // extend selection to include this new range
  1643.             //
  1644.         } else {
  1645.             if (mystart < galSelStart[j]) {
  1646.             galSelLen[j] += galSelStart[j] - mystart;
  1647.             galSelStart[j] = mystart;
  1648.             }
  1649.             if (mystart + mylength > galSelStart[j] + galSelLen[j])
  1650.             galSelLen[j] = mystart + mylength - galSelStart[j];
  1651.         }
  1652.         }
  1653.     }
  1654.     }
  1655. }
  1656.  
  1657. /*----------------------------------------------------------------------------*\
  1658. |    EditStreamName()                                                           |
  1659. |                                                                               |
  1660. |    We need to edit the name of a stream. Create the edit box.                 |
  1661. \*----------------------------------------------------------------------------*/
  1662.  
  1663. void EditStreamName(HWND hwndParent)
  1664.  
  1665. {
  1666.     AVISTREAMINFO avis;
  1667.  
  1668.     // Get the stream info so we can get the name
  1669.     AVIStreamInfo(gapavi[gSelectedStream-MAXNUMSTREAMS], &avis, sizeof(avis));
  1670.  
  1671.     // Create the window;
  1672.     InflateRect(&grcSelectedStream, 0, 2);
  1673.     ghwndEdit = CreateWindow(AVI_EDIT_CLASS, NULL,
  1674.                  WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
  1675.                  grcSelectedStream.left,
  1676.                  grcSelectedStream.top,
  1677.                  grcSelectedStream.right - grcSelectedStream.left,
  1678.                  grcSelectedStream.bottom - grcSelectedStream.top,
  1679.                  hwndParent, (HMENU)1, ghInstApp, NULL);
  1680.  
  1681.     // Subclass the window so we can trap <cr> hits.
  1682.     gOldEditProc = (WNDPROC)GetWindowLong(ghwndEdit, GWL_WNDPROC);
  1683.     SetWindowLong(ghwndEdit, GWL_WNDPROC, (long)NewEditProc);
  1684.  
  1685.     // Set the initial text of the edit window, give focus to the
  1686.     // window and select the text.
  1687.     SetWindowText(ghwndEdit, avis.szName);
  1688.     SetFocus(ghwndEdit);
  1689.     Edit_SetSel(ghwndEdit, 0, lstrlen(avis.szName));
  1690. }
  1691.  
  1692. /*----------------------------------------------------------------------------*\
  1693. |    EditDone()                                                                 |
  1694. |                                                                               |
  1695. |    Done with an edit. See if we take the changes.                             |
  1696. \*----------------------------------------------------------------------------*/
  1697.  
  1698. void EditDone(HWND hwndParent, BOOL bAcceptChange)
  1699. {
  1700.     // Update the stream name if we're supposed to.
  1701.     if (bAcceptChange) {
  1702.     char szBuff[BUFSIZE];
  1703.     int n;
  1704.  
  1705.     // Get the edited name and put into the stream header
  1706.     n = GetWindowText(ghwndEdit, szBuff, BUFSIZE);
  1707.     szBuff[n] = '\0';
  1708.     EditStreamSetName(gapavi[gSelectedStream-MAXNUMSTREAMS], szBuff);
  1709.     }
  1710.  
  1711.     // Turn the selection off.
  1712.     gSelectedStream = -1;
  1713.  
  1714.     // Nuke the edit window.
  1715.     SetWindowLong(ghwndEdit, GWL_WNDPROC, (long)gOldEditProc);
  1716.     DestroyWindow(ghwndEdit);
  1717.     ghwndEdit = NULL;
  1718.  
  1719.     // Paint where window used to be.
  1720.     InvalidateRect(hwndParent, &grcSelectedStream, TRUE);
  1721.     UpdateWindow(hwndParent);
  1722.  
  1723.     // Give the parent the focus back.
  1724.     SetFocus(hwndParent);
  1725. }
  1726.  
  1727. /*----------------------------------------------------------------------------*\
  1728. |    NewEditProc()                                                              |
  1729. |                                                                               |
  1730. |    Our own home-rolled window proc for the edit window giving                 |
  1731. |    notification when <cr> is hit.                                             |
  1732. |                                                                               |
  1733. |    We trap WM_CHAR, because WM_CHAR(wParam='\r') causes a beep, which         |
  1734. |    we don't want.                                                             |
  1735. \*----------------------------------------------------------------------------*/
  1736. #define _ANSI_R    (TCHAR)'\r'
  1737. LRESULT CALLBACK NewEditProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
  1738. {
  1739.     switch (msg) {
  1740.     case WM_CHAR:
  1741.         // Trap a keydown for <cr>
  1742.         if (wParam == _ANSI_R) {
  1743.         // Send message to parent giving ID 2 (which doesn't
  1744.         // exist)
  1745.             FORWARD_WM_COMMAND(GetParent(hwnd), 2, NULL, 0, PostMessage);
  1746.         return 0L;
  1747.         }
  1748.  
  1749.         // Need normal handling of characters otherwise
  1750.         goto callDWP;
  1751.  
  1752.     case WM_KEYUP:
  1753.         // Trap a keydown for <Esc>. This is how we get out without
  1754.         // making a modification.
  1755.         if (wParam == VK_ESCAPE) {
  1756.         // Send message to parent giving ID 3 (which doesn't
  1757.         // exist)
  1758.             FORWARD_WM_COMMAND(GetParent(hwnd), 3, NULL, 0, PostMessage);
  1759.         return 0L;
  1760.         }
  1761.  
  1762.         // We want to fall through so what should happen on keydown
  1763.         // does.
  1764.  
  1765.     default:
  1766. callDWP:
  1767.         // Just call the old window proc
  1768.         return CallWindowProc(gOldEditProc, hwnd, msg, wParam, lParam);
  1769.     }
  1770. }
  1771.  
  1772. // ******************************************************************************
  1773. //
  1774. //      Message Handler for WM_CREATE
  1775. //
  1776. // ******************************************************************************
  1777.  
  1778. BOOL App_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
  1779. {
  1780.     DragAcceptFiles(hwnd, TRUE);
  1781.  
  1782.     if (gszFileName[0])
  1783.     InitAvi(hwnd, gszFileName, MENU_OPEN);
  1784.  
  1785.     return TRUE;
  1786. }
  1787.  
  1788. // ******************************************************************************
  1789. //
  1790. //      Message Handler for WM_COMMAND
  1791. //
  1792. // ******************************************************************************
  1793.  
  1794. void App_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
  1795. {
  1796.     if (id == 2)
  1797.     EditDone(hwnd, TRUE);
  1798.     // See if we are terminating editting.
  1799.     else if (id == 3)
  1800.     EditDone(hwnd, FALSE);
  1801.     else
  1802.     MenuHandler( hwnd, id );
  1803. }
  1804.  
  1805. // ******************************************************************************
  1806. //
  1807. //      Message Handler for WM_DROPFILES
  1808. //
  1809. // ******************************************************************************
  1810. //
  1811. void App_OnDropFiles(HWND hwnd, HDROP hdrop)
  1812. {
  1813.     DropAvi(hwnd, hdrop);
  1814.  
  1815.     return;
  1816. }
  1817.  
  1818. // ******************************************************************************
  1819. //
  1820. //      Message Handler for WM_INITMENU
  1821. //
  1822. // ******************************************************************************
  1823. void App_OnInitMenu(HWND hwnd, HMENU hMenu)
  1824. {
  1825.     int i;
  1826.     BOOL f;
  1827.     PAVIFILE pf;
  1828.  
  1829.     f = gcpavi > 0;
  1830.     EnableMenuItem(hMenu, MENU_SAVEAS, f ? MF_ENABLED : MF_GRAYED);
  1831.     EnableMenuItem(hMenu, MENU_OPTIONS,f ? MF_ENABLED : MF_GRAYED);
  1832.  
  1833.     EnableMenuItem(hMenu, MENU_CLOSE,  f ? MF_ENABLED : MF_GRAYED);
  1834.     EnableMenuItem(hMenu, MENU_MERGE,  f ? MF_ENABLED : MF_GRAYED);
  1835.  
  1836.     EnableMenuItem(hMenu, MENU_SETINFO,  f ? MF_ENABLED : MF_GRAYED);
  1837.  
  1838.     // !!! Why not provide UNDO while I'm at it?
  1839.     // Enable CUT/COPY/DELETE if there's something selected in a stream
  1840.     f = FALSE;
  1841.     for (i=0; i<gcpavi; i++)
  1842.     if (galSelStart[i] != -1)
  1843.         f = TRUE;
  1844.  
  1845.     EnableMenuItem(hMenu, MENU_COPY,   f ? MF_ENABLED : MF_GRAYED);
  1846.     EnableMenuItem(hMenu, MENU_CUT,    f ? MF_ENABLED : MF_GRAYED);
  1847.     EnableMenuItem(hMenu, MENU_DELETE, f ? MF_ENABLED : MF_GRAYED);
  1848.  
  1849.     // If we haven't an edit window, we need to setup the "Name'
  1850.     //
  1851.     if (ghwndEdit == NULL)
  1852.     {
  1853.     LoadString( ghInstApp, IDS_NAME, gszBuffer, BUFSIZE );
  1854.     ModifyMenu(hMenu, MENU_NAME, MF_BYCOMMAND | MF_STRING, MENU_NAME, gszBuffer);
  1855.     EnableMenuItem(hMenu, MENU_NAME,
  1856.                (gSelectedStream >= MAXNUMSTREAMS) ? MF_ENABLED : MF_GRAYED);
  1857.     }
  1858.     else
  1859.     {
  1860.     LoadString( ghInstApp, IDS_ABORTNAME, gszBuffer, BUFSIZE );
  1861.     ModifyMenu(hMenu, MENU_NAME, MF_BYCOMMAND | MF_STRING,MENU_NAME, gszBuffer);
  1862.     EnableMenuItem(hMenu, MENU_NAME,MF_ENABLED);
  1863.     }
  1864.  
  1865.     // See if there's anything to paste....
  1866.     f = FALSE;
  1867.     AVIGetFromClipboard(&pf);
  1868.  
  1869.     if (pf)
  1870.     {
  1871.     f = TRUE;
  1872.     AVIFileRelease(pf);
  1873.     }
  1874.  
  1875.     EnableMenuItem(hMenu, MENU_PASTE,  f ? MF_ENABLED : MF_GRAYED);
  1876.  
  1877.     f = gfAudioFound | gfVideoFound;
  1878.     EnableMenuItem(hMenu, MENU_PREVIEW, (f & !gfPlaying) ? MF_ENABLED : MF_GRAYED);
  1879.     EnableMenuItem(hMenu, MENU_STOP,    (f & gfPlaying)  ? MF_ENABLED : MF_GRAYED);
  1880.  
  1881.     CheckMenuItem(hMenu, MENU_ZOOMQUARTER, (gwZoom == 1)  ? MF_CHECKED : MF_UNCHECKED);
  1882.     CheckMenuItem(hMenu, MENU_ZOOMHALF,    (gwZoom == 2)  ? MF_CHECKED : MF_UNCHECKED);
  1883.     CheckMenuItem(hMenu, MENU_ZOOM1,       (gwZoom == 4)  ? MF_CHECKED : MF_UNCHECKED);
  1884.     CheckMenuItem(hMenu, MENU_ZOOM2,       (gwZoom == 8)  ? MF_CHECKED : MF_UNCHECKED);
  1885.     CheckMenuItem(hMenu, MENU_ZOOM4,       (gwZoom == 16) ? MF_CHECKED : MF_UNCHECKED);
  1886.  
  1887.  
  1888.     return;
  1889. }
  1890. // ******************************************************************************
  1891. //
  1892. //      Message Handler for WM_SIZE
  1893. //
  1894. // ******************************************************************************
  1895.  
  1896. void App_OnSize(HWND hwnd, UINT state, int cx, int cy)
  1897. {
  1898.     RECT rc;
  1899.  
  1900.     GetClientRect(hwnd, &rc);
  1901.     //
  1902.     // There is not enough vertical room to show all streams. Scrollbars
  1903.     // are required.
  1904.     //
  1905.     if (nVertHeight > rc.bottom)
  1906.     {
  1907.     nVertSBLen = nVertHeight - rc.bottom;
  1908.     SetScrollRange(hwnd, SB_VERT, 0, nVertSBLen, TRUE);
  1909.     }
  1910.     else// Everything fits vertically. No scrollbar necessary.
  1911.     {
  1912.     nVertSBLen = 0;
  1913.     SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
  1914.     }
  1915.     return;
  1916. }
  1917. // ******************************************************************************
  1918. //
  1919. //      Message Handler for WM_DESTROY
  1920. //
  1921. // ******************************************************************************
  1922. //
  1923. void App_OnDestroy(HWND hwnd)
  1924. {
  1925.     AVIClearClipboard();
  1926.     FreeAvi(hwnd);      // close all open streams
  1927.     AVIFileExit();      // shuts down the AVIFile system
  1928.     PostQuitMessage(0);
  1929.  
  1930.     return;
  1931. }
  1932. // ******************************************************************************
  1933. //
  1934. //      Message Handler for WM_ENDSESSION
  1935. //
  1936. // ******************************************************************************
  1937. //
  1938. void App_OnEndSession(HWND hwnd, BOOL fEnding)
  1939. {
  1940.     if (fEnding)
  1941.     {
  1942.     if (GetClipboardOwner() == hwnd)
  1943.     {
  1944.         if (OpenClipboard(hwnd))
  1945.         {
  1946.         EmptyClipboard();
  1947.         CloseClipboard();
  1948.         }
  1949.     }
  1950.     FreeAvi(hwnd);
  1951.     }
  1952.     return;
  1953. }
  1954. // ******************************************************************************
  1955. //
  1956. //      Message Handler for WM_PALETTECHANGED
  1957. //
  1958. // ******************************************************************************
  1959. //
  1960. void App_OnPaletteChanged(HWND hwnd, HWND hwndPaletteChange)
  1961. {
  1962.     // It came from us.  Ignore it
  1963.     if (hwndPaletteChange == hwnd)
  1964.     return;
  1965.  
  1966.     //if needed, insert any calls - such as RealizePallete - below
  1967.  
  1968.     return;
  1969. }
  1970. // ******************************************************************************
  1971. //
  1972. //      Message Handler for WM_KEYDOWN
  1973. //
  1974. // ******************************************************************************
  1975. //
  1976. void App_OnKey(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags)
  1977. {
  1978.     if(fDown)
  1979.     {
  1980.     switch (vk)
  1981.     {
  1982.         case VK_UP:
  1983.         FORWARD_WM_VSCROLL(hwnd, NULL, SB_LINEUP, 0, PostMessage);
  1984.         break;
  1985.         case VK_DOWN:
  1986.         FORWARD_WM_VSCROLL(hwnd, NULL, SB_LINEDOWN, 0, PostMessage);
  1987.         break;
  1988.         case VK_PRIOR:
  1989.         FORWARD_WM_HSCROLL(hwnd, NULL, SB_PAGEUP, 0, PostMessage);
  1990.         break;
  1991.         case VK_NEXT:
  1992.         FORWARD_WM_HSCROLL(hwnd, NULL, SB_PAGEDOWN, 0, PostMessage);
  1993.         break;
  1994.         case VK_HOME:
  1995.         FORWARD_WM_HSCROLL(hwnd, NULL, SB_THUMBPOSITION, 0, PostMessage);
  1996.         break;
  1997.         case VK_END:
  1998.         FORWARD_WM_HSCROLL(hwnd, NULL, SB_THUMBPOSITION, 0x7FFF, PostMessage);
  1999.         break;
  2000.         case VK_LEFT:
  2001.         FORWARD_WM_HSCROLL(hwnd, NULL, SB_LINEUP, 0, PostMessage);
  2002.         break;
  2003.         case VK_RIGHT:
  2004.         FORWARD_WM_HSCROLL(hwnd, NULL, SB_LINEDOWN, 0, PostMessage);
  2005.         break;
  2006.     }
  2007.     }
  2008.     return;
  2009. }
  2010. // ******************************************************************************
  2011. //
  2012. //      Message Handler for WM_HSCROLL
  2013. //
  2014. // ******************************************************************************
  2015. //
  2016. void App_OnHScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos)
  2017. {
  2018.     LONG lScrollTime;
  2019.  
  2020.     lScrollTime = GetScrollTime(hwnd);
  2021.  
  2022.     switch (code)
  2023.     {
  2024.     case SB_LINEDOWN:
  2025.         lScrollTime += timehscroll;
  2026.         break;
  2027.     case SB_LINEUP:
  2028.         lScrollTime -= timehscroll;
  2029.         break;
  2030.     case SB_PAGEDOWN:
  2031.         lScrollTime += timeLength/10;
  2032.         break;
  2033.     case SB_PAGEUP:
  2034.         lScrollTime -= timeLength/10;
  2035.         break;
  2036.     case SB_THUMBTRACK:
  2037.     case SB_THUMBPOSITION:
  2038.         lScrollTime = pos;
  2039.         lScrollTime = timeStart + muldiv32(lScrollTime, timeLength, SCROLLRANGE);
  2040.         break;
  2041.     }
  2042.  
  2043.     if (lScrollTime < timeStart)
  2044.     lScrollTime = timeStart;
  2045.  
  2046.     if (lScrollTime > timeEnd)
  2047.     lScrollTime = timeEnd;
  2048.  
  2049.     if (lScrollTime == (LONG)GetScrollTime(hwnd))
  2050.     return;
  2051.  
  2052.     SetScrollTime(hwnd, lScrollTime);
  2053.     InvalidateRect(hwnd, NULL, TRUE);
  2054.     UpdateWindow(hwnd);
  2055.  
  2056.     return;
  2057. }
  2058. // ******************************************************************************
  2059. //
  2060. //      Message Handler for WM_VSCROLL
  2061. //
  2062. // ******************************************************************************
  2063. //
  2064. void App_OnVScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos)
  2065. {
  2066.     int nScrollPos;
  2067.     RECT rc;
  2068.  
  2069.     nScrollPos = GetScrollPos(hwnd, SB_VERT);
  2070.  
  2071.     GetClientRect(hwnd, &rc);
  2072.  
  2073.     switch (code)
  2074.     {
  2075.     case SB_LINEDOWN:
  2076.         nScrollPos += 10;
  2077.         break;
  2078.     case SB_LINEUP:
  2079.         nScrollPos -= 10;
  2080.         break;
  2081.     case SB_PAGEDOWN:
  2082.         nScrollPos += rc.bottom;
  2083.         break;
  2084.     case SB_PAGEUP:
  2085.         nScrollPos -= rc.bottom;
  2086.         break;
  2087.     case SB_THUMBTRACK:
  2088.     case SB_THUMBPOSITION:
  2089.         nScrollPos = pos;
  2090.         break;
  2091.     }
  2092.  
  2093.     if (nScrollPos < 0)
  2094.     nScrollPos = 0;
  2095.  
  2096.     if (nScrollPos > nVertSBLen)
  2097.     nScrollPos = nVertSBLen;
  2098.  
  2099.     if (nScrollPos == GetScrollPos(hwnd, SB_VERT))
  2100.     return;
  2101.  
  2102.     SetScrollPos(hwnd, SB_VERT, nScrollPos, TRUE);
  2103.     InvalidateRect(hwnd, NULL, TRUE);
  2104.     UpdateWindow(hwnd);
  2105.  
  2106.     return;
  2107. }
  2108. // ******************************************************************************
  2109. //
  2110. //      Message Handler for WM_RBUTTONDOWN
  2111. //
  2112. // ******************************************************************************
  2113. //
  2114. void App_OnRButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
  2115. {
  2116.     RECT rc;
  2117.     int i;
  2118.     // Invalidate any stream that has something selected.  It needs
  2119.     // to redraw now.
  2120.     if (gSelectedStream >= MAXNUMSTREAMS) {
  2121.     if (ghwndEdit)
  2122.         EditDone(hwnd, FALSE);
  2123.     else {
  2124.         InvalidateRect(hwnd, &grcSelectedStream, TRUE); // needs to erase
  2125.         gSelectedStream = -1;
  2126.     }
  2127.     }
  2128.     else {
  2129.     GetClientRect(hwnd, &rc);
  2130.     for (i = 0; i < gcpavi; i++) {
  2131.          if (galSelStart[i] != -1) {
  2132.         rc.top = gStreamTop[i] - GetScrollPos(hwnd, SB_VERT);
  2133.         rc.bottom =gStreamTop[i+1] -GetScrollPos(hwnd, SB_VERT);
  2134.         InvalidateRect(hwnd, &rc, TRUE); // needs to erase
  2135.         }
  2136.     }
  2137.     
  2138.     // Deselect everything
  2139.     SelectStream(hwnd, -1, -1, -1, FALSE, FALSE);
  2140.     }
  2141.     return;
  2142. }
  2143. // ******************************************************************************
  2144. //
  2145. //      Message Handler for WM_LBUTTONDOWN
  2146. //
  2147. // ******************************************************************************
  2148. //
  2149. void App_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
  2150. {
  2151.     int yTop, yBottom = 0, iFrameWidth, nFrames;
  2152.     int i,j,n;
  2153.     AVISTREAMINFO       avis;
  2154.     BOOL fShift = FALSE;
  2155.     BOOL fCtrl = FALSE;
  2156.     RECT rc, rcC;
  2157.     LONG        l, lTime, lSamp, lCurSamp;
  2158.  
  2159.     if( fDoubleClick ) {
  2160.     // See if we get to edit a stream name
  2161.     if (gSelectedStream >= MAXNUMSTREAMS)
  2162.         EditStreamName(hwnd);
  2163.     
  2164.     return;
  2165.     }
  2166.  
  2167.     GetClientRect(hwnd, &rc);
  2168.     rcC = rc;
  2169.     yBottom = -GetScrollPos(hwnd, SB_VERT);     // offset for scrollbar
  2170.  
  2171.     // If we currently have a stream name selected, we must
  2172.     // deselect it.
  2173.     if (gSelectedStream >= MAXNUMSTREAMS) {
  2174.     // Erase the select marks
  2175.     if (ghwndEdit)
  2176.         EditDone(hwnd, FALSE);
  2177.     else
  2178.         InvalidateRect(hwnd, &grcSelectedStream, TRUE);
  2179.     
  2180.     // Stream no longer selected.
  2181.     gSelectedStream = -1;
  2182.     }
  2183.     else {
  2184.     //
  2185.     // Otherwise, if the shift key isn't down,
  2186.     // we deselect everything first
  2187.     //
  2188.     fShift = GetAsyncKeyState(VK_SHIFT) & 0x8000;
  2189.     fCtrl = GetAsyncKeyState(VK_CONTROL) & 0x8000;
  2190.     if (!fShift) {
  2191.         // Invalidate any stream that has something selected.  It needs
  2192.         // to redraw now.
  2193.         for (j = 0; j < gcpavi; j++) {
  2194.         if (galSelStart[j] != -1) {
  2195.             rc.top = gStreamTop[j] - GetScrollPos(hwnd, SB_VERT);
  2196.             rc.bottom =gStreamTop[j+1] -GetScrollPos(hwnd, SB_VERT);
  2197.             InvalidateRect(hwnd, &rc, TRUE); // needs to erase
  2198.         }
  2199.         }
  2200.         // Deselect everything
  2201.         SelectStream(hwnd, -1, -1, -1, FALSE, FALSE);
  2202.     }
  2203.     }
  2204.  
  2205.  
  2206.     //
  2207.     // Walk the streams and find out where we clicked
  2208.     //
  2209.     for (i=0; i<gcpavi; i++) {
  2210.     AVIStreamInfo(gapavi[i], &avis, sizeof(avis));
  2211.     
  2212.     // See if they clicked on the name
  2213.     yTop = gStreamTop[i] - GetScrollPos(hwnd, SB_VERT);
  2214.     yBottom = yTop + (2 * TSPACE);  // !!! size of stream header
  2215.     
  2216.     if (y >= yTop && y < yBottom) {
  2217.     
  2218.         gSelectedStream = MAXNUMSTREAMS+i;  // which stream's header it is
  2219.         rc.top = yTop;
  2220.         rc.bottom = yBottom;
  2221.         grcSelectedStream = rc;     // Invalidate this on button up
  2222.     
  2223.         InvalidateRect(hwnd, &rc, FALSE);   // repaint whole strip
  2224.     
  2225.         // No need to be here anymore
  2226.         return;
  2227.     }
  2228.     
  2229.     
  2230.     //*******************************************************************
  2231.     // See if they clicked on the information Header
  2232.     yTop = yBottom;
  2233.     yBottom = yTop + (2 * TSPACE);  // !!! size of stream header
  2234.     
  2235.     //
  2236.     // If they've clicked on the header - select the whole stream
  2237.     //
  2238.     if (y >= yTop && y < yBottom) {
  2239.     
  2240.         // Maybe select everything if Ctrl is held down
  2241.         SelectStream(hwnd, i, AVIStreamStart(gapavi[i]),
  2242.              AVIStreamEnd(gapavi[i]), FALSE, fCtrl);
  2243.     
  2244.         // Tell paint code to highlight the text area.  Invalidate
  2245.         // the whole stream area.  When they let go of the mouse,
  2246.         // redraw the text area only.
  2247.         gSelectedStream = i;        // which stream's header it is
  2248.         rc.top = yTop;
  2249.         rc.bottom = yBottom;
  2250.         grcSelectedStream = rc;     // Invalidate this on button up
  2251.         // Now get the area of the whole stream
  2252.         rc.bottom = gStreamTop[i + 1] - GetScrollPos(hwnd, SB_VERT);
  2253.         InvalidateRect(hwnd, &rc, FALSE);   // repaint whole strip
  2254.     
  2255.         // Time to go
  2256.         return;
  2257.     }
  2258.     
  2259.     // *******************************************************************
  2260.     
  2261.     //
  2262.     // Now get the area of the stream data, and...
  2263.     //
  2264.     yTop = yBottom;
  2265.     yBottom = gStreamTop[i + 1] - GetScrollPos(hwnd, SB_VERT);
  2266.     
  2267.     //
  2268.     // ... see if we clicked on a video frame, or...
  2269.     //
  2270.     if (avis.fccType == streamtypeVIDEO) {
  2271.         if (gapgf[i] == NULL)
  2272.         continue;
  2273.     
  2274.         //
  2275.         // We're in the vertical range of the strip of video
  2276.         //
  2277.         if (y >= yTop && y < yBottom) {
  2278.         
  2279.         rc.top = yTop; rc.bottom = yBottom;
  2280.         // Time at the centre of the strip
  2281.         lTime = GetScrollTime(hwnd);
  2282.         // What frame should appear in the centre? Times that
  2283.         // are too big will all return the last frame, so we
  2284.         // need to calculate the hypothetical frame number
  2285.         if (lTime <= AVIStreamEndTime(gapavi[i])) {
  2286.             lSamp = AVIStreamTimeToSample(gapavi[i], lTime);
  2287.         }
  2288.         else {
  2289.             lSamp = AVIStreamEnd(gapavi[i]) +
  2290.                 AVIStreamTimeToSample(gapavi[i],
  2291.                           lTime - AVIStreamEndTime(gapavi[i]));
  2292.         }
  2293.         // How wide is each frame?
  2294.         iFrameWidth = (avis.rcFrame.right - avis.rcFrame.left) *
  2295.                   gwZoom / 4 + HSPACE;    // !!! hacky constant
  2296.         // How many frames on each half of centre?
  2297.         nFrames = (rcC.right - iFrameWidth) / (2 * iFrameWidth);
  2298.         if (nFrames < 0)
  2299.             nFrames = 0;        // at least show *something*
  2300.         
  2301.         //
  2302.         // Walk all frames and find which one we're on top of
  2303.         //
  2304.         for (n = -nFrames; n <= nFrames; n++) {
  2305.         
  2306.             rc.left   = rcC.right / 2 -
  2307.                 (avis.rcFrame.right * gwZoom / 4) / 2 +
  2308.                 (n * iFrameWidth);
  2309.             rc.right = rc.left + iFrameWidth;
  2310.         
  2311.             //
  2312.             // We're on top of this frame!
  2313.             //
  2314.             if (x >= rc.left && x < rc.right) {
  2315.             //
  2316.             // For the top video stream, it's easy to tell
  2317.             // which frame we're on... each frame is
  2318.             // displayed in order.
  2319.             //
  2320.             if (i == giFirstVideo)
  2321.                 SelectStream(hwnd, i, lSamp + n, 1, fShift, fCtrl);
  2322.             
  2323.             
  2324.             //
  2325.             // For other video streams, we need to calculate
  2326.             // the time of the spot we're on, and see which
  2327.             // frame is associated with it, because who
  2328.             // knows what scale we're using for time.
  2329.             //
  2330.             else {
  2331.                 l = lTime + MulDiv32(n * iFrameWidth, gdwMicroSecPerPixel, 1000);
  2332.                 lCurSamp = AVIStreamTimeToSample( gapavi[i], l);
  2333.                 SelectStream(hwnd, i, lCurSamp, 1, fShift, fCtrl);
  2334.             }
  2335.             
  2336.             //
  2337.             // Invalidate what we'll be highlighting.
  2338.             // This includes the text area above.  If
  2339.             // we're adding to a selection, invalidate the
  2340.             // whole strip because other frames might
  2341.             // become selected by this, and the text
  2342.             // changes.
  2343.             //
  2344.             InflateRect(&rc, HSPACE / 2, VSPACE / 2);
  2345.             if (fShift) {       // could select more than this
  2346.                 rc.left = 0;
  2347.                 rc.right = rcC.right;
  2348.             }
  2349.             InvalidateRect(hwnd, &rc, FALSE);
  2350.             // Now invalidate the text area
  2351.             rc.bottom = rc.top;
  2352.             rc.top -= (2 * TSPACE); // !!! text changes
  2353.             rc.left = 0;
  2354.             rc.right = rcC.right;
  2355.             // If we've got selection text already, erase
  2356.             InvalidateRect(hwnd, &rc, fShift);
  2357.             return;
  2358.             }
  2359.         }//end of inner frame walk for statement
  2360.         }
  2361.     }
  2362.     
  2363.     // ********************************************************************
  2364.     
  2365.     //
  2366.     // ... see if we clicked on an audio section
  2367.     //
  2368.     else if (avis.fccType == streamtypeAUDIO) {
  2369.     
  2370.         //
  2371.         // We clicked inside the wave!
  2372.         //
  2373.         if (y >= yTop && y < yBottom) {
  2374.         
  2375.         //
  2376.         // Get the time we clicked on, and it's sample number
  2377.         //
  2378.         GetClientRect(hwnd, &rcC);
  2379.         lTime = GetScrollTime(hwnd);
  2380.         if (x < (rcC.right/2)) { //button down to the left of current position
  2381.             l = lTime - muldiv32( (rcC.right / 2) - x, gdwMicroSecPerPixel, 1000);
  2382.         }
  2383.         else {
  2384.             l = lTime + muldiv32(x - rcC.right / 2, gdwMicroSecPerPixel, 1000);
  2385.         }
  2386.         lCurSamp = AVIStreamTimeToSample(gapavi[i], l);
  2387.         
  2388.         SelectStream(hwnd, i, lCurSamp, 1, fShift, fCtrl);
  2389.         
  2390.         // Invalidate what we'll need to repaint to show it
  2391.         rc.left = x - 2;        // may not be exact right pixel
  2392.         rc.right = x + 3;
  2393.         rc.top = yTop;
  2394.         rc.bottom = yBottom;
  2395.         InflateRect(&rc, 0, VSPACE / 2);
  2396.         // If we're adding to a selection, we better invalidate
  2397.         // the whole strip.
  2398.         if (fShift) {
  2399.             rc.left = 0;
  2400.             rc.right = rcC.right;
  2401.         }
  2402.         InvalidateRect(hwnd, &rc, FALSE);
  2403.         // Now invalidate the header text
  2404.         rc.bottom = rc.top;
  2405.         rc.top -= (2 * TSPACE); // !!! text changes
  2406.         rc.left = 0;
  2407.         rc.right = rcC.right;
  2408.         // If we've got selection text already, erase
  2409.         InvalidateRect(hwnd, &rc, fShift);
  2410.         }
  2411.     }
  2412.     }//end of stream walk for statement
  2413.  
  2414.  
  2415.     return;
  2416. }
  2417. // ****************************************************************************
  2418. //
  2419. //      Message Handler for WM_LBUTTONUP
  2420. //
  2421. // ****************************************************************************
  2422. //
  2423. void App_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
  2424. {
  2425.     //
  2426.     // If we're selecting a whole stream, stop highlighting the text area
  2427.     //
  2428.     if ((gSelectedStream >= 0) && (gSelectedStream < MAXNUMSTREAMS))
  2429.     {
  2430.     InvalidateRect(hwnd, &grcSelectedStream, TRUE);
  2431.     gSelectedStream = -1;
  2432.     }
  2433.     return;
  2434. }
  2435. /*----------------------------------------------------------------------------*\
  2436. |   AppWndProc( hwnd, uiMessage, wParam, lParam )                              |
  2437. |                                                                              |
  2438. |   Description:                                                               |
  2439. |       The window proc for the app's main (tiled) window.  This processes all |
  2440. |       of the parent window's messages.                                       |
  2441. |                                                                              |
  2442. |   Arguments:                                                                 |
  2443. |       hwnd            window handle for the window                           |
  2444. |       uiMessage       message number                                         |
  2445. |       wParam          message-dependent                                      |
  2446. |       lParam          message-dependent                                      |
  2447. |                                                                              |
  2448. |   Returns:                                                                   |
  2449. |       0 if processed, nonzero if ignored                                     |
  2450. |                                                                              |
  2451. \*----------------------------------------------------------------------------*/
  2452. LRESULT CALLBACK AppWndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  2453. {
  2454.     PAINTSTRUCT ps;
  2455.     BOOL        f;
  2456.     HDC         hdc;
  2457.  
  2458.     switch (msg) {
  2459.     
  2460.     //
  2461.     // If we passed a command line filename, open it
  2462.     //
  2463.     case WM_CREATE:
  2464.         return HANDLE_WM_CREATE(hwnd, wParam, lParam, App_OnCreate);
  2465.     
  2466.     case WM_COMMAND:
  2467.         HANDLE_WM_COMMAND(hwnd, wParam, lParam, App_OnCommand);
  2468.         break;
  2469.     
  2470.     case WM_DROPFILES:
  2471.         HANDLE_WM_DROPFILES(hwnd, wParam, lParam, App_OnDropFiles);
  2472.         break;
  2473.     
  2474.     case WM_INITMENU:
  2475.         HANDLE_WM_INITMENU(hwnd, wParam, lParam, App_OnInitMenu);
  2476.         break;
  2477.     
  2478.         //
  2479.         // During a wait state (eg saving) don't let us choose any menus
  2480.         //
  2481.     case WM_NCHITTEST:
  2482.     
  2483.         if (gfWait)
  2484.         {
  2485.         // Let windows tell us where the cursor is
  2486.         lParam = DefWindowProc(hwnd,msg,wParam,lParam);
  2487.         
  2488.         // If it's over a menu, pretend it's in the client (force
  2489.         // hourglass)
  2490.         if (lParam == HTMENU)
  2491.             lParam = HTCLIENT;
  2492.         
  2493.         return lParam;
  2494.         }
  2495.         break;
  2496.         //
  2497.         // Set vertical scrollbar for scrolling streams
  2498.         //
  2499.     case WM_SIZE:
  2500.         HANDLE_WM_SIZE(hwnd, wParam, lParam, App_OnSize);
  2501.         break;
  2502.     
  2503.         //
  2504.         // During a wait state, show an hourglass over our client area
  2505.         // !!! Is this necessary?
  2506.         //
  2507.     case WM_SETCURSOR:
  2508.         if (gfWait && LOWORD(lParam) == HTCLIENT)
  2509.         {
  2510.         SetCursor(LoadCursor(NULL, IDC_WAIT));
  2511.         return TRUE;
  2512.         }
  2513.         break;
  2514.     
  2515.         //
  2516.         // We're out of here!
  2517.         //
  2518.     case WM_DESTROY:
  2519.         HANDLE_WM_DESTROY(hwnd, wParam, lParam, App_OnDestroy);\
  2520.             break;
  2521.     
  2522.     case WM_ENDSESSION:
  2523.         HANDLE_WM_ENDSESSION(hwnd, wParam, lParam, App_OnEndSession);
  2524.         break;
  2525.     
  2526.         //
  2527.         // Don't let us close ourselves in a wait state (eg saving)
  2528.         //
  2529.     case WM_CLOSE:
  2530.         if (gfWait)
  2531.         return 0;
  2532.         break;
  2533.     
  2534.         //
  2535.         // Block keyboard access to menus if waiting
  2536.         //
  2537.     case WM_SYSCOMMAND:
  2538.         switch (wParam & 0xFFF0) {
  2539.         case SC_KEYMENU:
  2540.             if (gfWait)
  2541.             return 0;
  2542.             break;
  2543.         }
  2544.         break;
  2545.     
  2546.     case WM_PALETTECHANGED:
  2547.         HANDLE_WM_PALETTECHANGED(hwnd, wParam, lParam, App_OnPaletteChanged);
  2548.         break;
  2549.         
  2550.     case WM_QUERYNEWPALETTE:
  2551.  
  2552.         if (gfVideoFound) {
  2553.         hdc = GetDC(hwnd);
  2554.         //
  2555.         // Realize the palette of the first video stream
  2556.         //
  2557.         if (f = DrawDibRealize(ghdd[giFirstVideo], hdc, FALSE))
  2558.             InvalidateRect(hwnd,NULL,TRUE);
  2559.  
  2560.         ReleaseDC(hwnd,hdc);
  2561.         return f;
  2562.         }
  2563.         break;
  2564.         
  2565.     case WM_ERASEBKGND:
  2566.         break;
  2567.         
  2568.     case WM_PAINT:
  2569.         hdc = BeginPaint(hwnd,&ps);
  2570.         
  2571.         PaintStuff(hdc, hwnd, FALSE);
  2572.         
  2573.         EndPaint(hwnd,&ps);
  2574.         break;
  2575.         
  2576.     //
  2577.     // handle the keyboard interface
  2578.     //
  2579.     case WM_KEYDOWN:
  2580.         HANDLE_WM_KEYDOWN(hwnd, wParam, lParam, App_OnKey);
  2581.         break;
  2582.         
  2583.     case WM_HSCROLL:
  2584.         HANDLE_WM_HSCROLL(hwnd, wParam, lParam, App_OnHScroll);
  2585.         break;
  2586.         
  2587.     case WM_VSCROLL:
  2588.         HANDLE_WM_VSCROLL(hwnd, wParam, lParam, App_OnVScroll);
  2589.         break;
  2590.         
  2591.     //
  2592.     // Deselect everything
  2593.     //
  2594.     case WM_RBUTTONDOWN:
  2595.         HANDLE_WM_RBUTTONDOWN(hwnd, wParam, lParam, App_OnRButtonDown);
  2596.         break;
  2597.         
  2598.     //
  2599.     // Select something
  2600.     //
  2601.     case WM_LBUTTONDOWN:
  2602.         HANDLE_WM_LBUTTONDOWN(hwnd, wParam, lParam, App_OnLButtonDown);
  2603.         break;
  2604.         
  2605.     case WM_LBUTTONDBLCLK:
  2606.         HANDLE_WM_LBUTTONDBLCLK(hwnd, wParam, lParam, App_OnLButtonDown);
  2607.         break;
  2608.         
  2609.     case WM_LBUTTONUP:
  2610.         HANDLE_WM_LBUTTONUP(hwnd, wParam, lParam, App_OnLButtonUp);
  2611.         break;
  2612.         
  2613.     //
  2614.     // Wave driver wants to tell us something.  Pass it on.
  2615.     //
  2616.     case MM_WOM_OPEN:
  2617.     case MM_WOM_DONE:
  2618.     case MM_WOM_CLOSE:
  2619.         aviaudioMessage(hwnd, msg, wParam, lParam);
  2620.         break;
  2621.     }
  2622.     return DefWindowProc(hwnd,msg,wParam,lParam);
  2623. }
  2624.  
  2625. /*----------------------------------------------------------------------------*\
  2626. |    SaveCallback()                                                             |
  2627. |                                                                               |
  2628. |    Our save callback that prints our progress in our window title bar         |
  2629. \*----------------------------------------------------------------------------*/
  2630. BOOL CALLBACK SaveCallback(int iProgress)
  2631. {
  2632.     char    szText[3*BUFSIZE];
  2633.     char    szFormat[BUFSIZE];
  2634.  
  2635.     LoadString( ghInstApp, IDS_APPNAME, gszBuffer, BUFSIZE );
  2636.     LoadString( ghInstApp, IDS_SAVEFORMAT, szFormat, BUFSIZE );
  2637.  
  2638.     wsprintf(szText, szFormat, (LPSTR) gszBuffer, (LPSTR) gszSaveFileName, iProgress);
  2639.  
  2640.     SetWindowText(ghwndApp, szText);
  2641.  
  2642.     //
  2643.     // Give ourselves a chance to abort
  2644.     //
  2645.     return WinYield();
  2646. }
  2647.  
  2648.  
  2649. /*----------------------------------------------------------------------------*\
  2650. |    GetDlgItemLong()                                                           |
  2651. |                                                                               |
  2652. |    Get a long integer from a dialog item.                                     |
  2653. \*----------------------------------------------------------------------------*/
  2654.  
  2655. DWORD GetDlgItemLong (HWND hwnd,  int idCtl, LPINT lpfOK, BOOL fSigned)
  2656. {
  2657.     LONG        l;
  2658.     char        ch;
  2659.     BOOL        fNegative = FALSE;
  2660.     char        szBuf[64];
  2661.     LPSTR       pbuf = szBuf;
  2662.     BOOL        fOk;
  2663.  
  2664.  
  2665.     fOk = FALSE;
  2666.     if (lpfOK)
  2667.     *lpfOK = FALSE;
  2668.  
  2669.     if (!GetDlgItemText(hwnd, idCtl, (LPSTR)szBuf, sizeof(szBuf)-1))
  2670.     return(0);
  2671.  
  2672.     while (*pbuf == ' ') pbuf++;
  2673.  
  2674.     if (fSigned && *pbuf == '-')
  2675.     {
  2676.     pbuf++;
  2677.     fNegative = TRUE;
  2678.     }
  2679.  
  2680.     l = 0;
  2681.     while ((ch = *pbuf++) >= '0' && ch <= '9')
  2682.     {
  2683.     fOk = TRUE;
  2684.     if (l > (DWORD)(ULONG_MAX/10))
  2685.         return(0);
  2686.     l = (l * 10) + ch - '0';
  2687.     if (fSigned && l > (DWORD)(ULONG_MAX/2))
  2688.         return(0);
  2689.     }
  2690.  
  2691.     if (fNegative)
  2692.     l = -l;
  2693.  
  2694.     if (lpfOK)
  2695.     *lpfOK = (ch == 0 && fOk);
  2696.  
  2697.     return(l);
  2698. }
  2699.  
  2700. /*----------------------------------------------------------------------------*\
  2701. |    SetDlgItemLong()                                                           |
  2702. |                                                                               |
  2703. |    Put a long integer into a dialog item.                                     |
  2704. \*----------------------------------------------------------------------------*/
  2705.  
  2706. void SetDlgItemLong (HWND hwnd, int idCtl, DWORD dwValue, BOOL fSigned )
  2707. {
  2708.     char szBuf[64];
  2709.  
  2710.     wsprintf (szBuf, fSigned ? "%ld" : "%lu", dwValue);
  2711.     SetDlgItemText(hwnd, idCtl, szBuf);
  2712. }
  2713.  
  2714.  
  2715. /*----------------------------------------------------------------------------*\
  2716. |    DoDataExchange()                                                           |
  2717. |                                                                               |
  2718. |    Exchange data between our internal buffer and dialog controls              |
  2719. |    fDir = TRUE for DialogBox->Buffer                                          |
  2720. |         = FALSE for Buffer->DialogBox                                         |
  2721. \*----------------------------------------------------------------------------*/
  2722.  
  2723. void DoDataExchange (HWND hwnd, LPAVISTREAMINFO lpinfo, BOOL fDir)
  2724. {
  2725.     if (fDir) {
  2726.     lpinfo->wPriority       = (WORD)GetDlgItemInt (hwnd, IDC_PRIORITY,    NULL, FALSE);
  2727.     lpinfo->wLanguage       = (WORD)GetDlgItemInt (hwnd, IDC_LANGUAGE,    NULL, FALSE);
  2728.     lpinfo->dwScale         = GetDlgItemLong(hwnd, IDC_SCALE,       NULL, FALSE);
  2729.     lpinfo->dwRate          = GetDlgItemLong(hwnd, IDC_RATE,        NULL, FALSE);
  2730.     lpinfo->dwStart         = GetDlgItemLong(hwnd, IDC_START,       NULL, FALSE);
  2731.     lpinfo->dwQuality       = GetDlgItemLong(hwnd, IDC_QUALITY,     NULL, FALSE);
  2732.     lpinfo->rcFrame.top     = GetDlgItemInt (hwnd, IDC_FRAMETOP,    NULL, TRUE);
  2733.     lpinfo->rcFrame.bottom  = GetDlgItemInt (hwnd, IDC_FRAMEBOTTOM, NULL, TRUE);
  2734.     lpinfo->rcFrame.left    = GetDlgItemInt (hwnd, IDC_FRAMELEFT,   NULL, TRUE);
  2735.     lpinfo->rcFrame.right   = GetDlgItemInt (hwnd, IDC_FRAMERIGHT,  NULL, TRUE);
  2736.     GetDlgItemText(hwnd, IDC_NAME, lpinfo->szName, sizeof(lpinfo->szName)-1);
  2737.     }
  2738.     else
  2739.     {
  2740.     SetDlgItemInt (hwnd, IDC_PRIORITY,    lpinfo->wPriority,       FALSE);
  2741.     SetDlgItemInt (hwnd, IDC_LANGUAGE,    lpinfo->wLanguage,       FALSE);
  2742.     SetDlgItemLong(hwnd, IDC_SCALE,       lpinfo->dwScale,         FALSE);
  2743.     SetDlgItemLong(hwnd, IDC_RATE,        lpinfo->dwRate,          FALSE);
  2744.     SetDlgItemLong(hwnd, IDC_START,       lpinfo->dwStart,         FALSE);
  2745.     SetDlgItemLong(hwnd, IDC_QUALITY,     lpinfo->dwQuality,       FALSE);
  2746.     SetDlgItemLong(hwnd, IDC_FRAMETOP,    lpinfo->rcFrame.top,     TRUE);
  2747.     SetDlgItemLong(hwnd, IDC_FRAMEBOTTOM, lpinfo->rcFrame.bottom,  TRUE);
  2748.     SetDlgItemLong(hwnd, IDC_FRAMELEFT,   lpinfo->rcFrame.left,    TRUE);
  2749.     SetDlgItemLong(hwnd, IDC_FRAMERIGHT,  lpinfo->rcFrame.right,   TRUE);
  2750.     SetDlgItemText(hwnd, IDC_NAME, lpinfo->szName);
  2751.     }
  2752. }
  2753.  
  2754. // *****************************************************************************
  2755. //
  2756. //      FUNCTION: Dlg_DefProc(HWND, UINT, WPARAM, LPARAM)
  2757. //
  2758. //      PURPOSE:  Handles default messages for all dialog boxes
  2759. //
  2760. //
  2761. static LRESULT Dlg_DefProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
  2762. {
  2763.     return DefDlgProcEx( hDlg, message, wParam, lParam, &gfDefDlgEx );
  2764. }
  2765. //
  2766. // *****************************************************************************
  2767. //
  2768. //      FUNCTION: InfoDlg_OnInitDialog(HWND, HWND, LPARAM)
  2769. //
  2770. //      PURPOSE:  Handles initialization for dialog box
  2771. //
  2772. //
  2773. static BOOL InfoDlg_OnInitDialog(HWND hDlg, HWND hwndFocus, LPARAM lParam)
  2774. {
  2775.     int i;
  2776.     glpavisi = (LPAVISTREAMINFO)
  2777.            GlobalAllocPtr (GHND, sizeof(AVISTREAMINFO)*gcpavi);
  2778.  
  2779.     for ( i = 0; i < gcpavi; ++i)
  2780.     {
  2781.     AVIStreamInfo(gapavi[i], &glpavisi[i], sizeof(AVISTREAMINFO));
  2782.     SendDlgItemMessage(hDlg, IDC_STREAMS, CB_ADDSTRING, 0, (LPARAM)(LPSTR)glpavisi[i].szName);
  2783.     }
  2784.     SendDlgItemMessage(hDlg, IDC_STREAMS, CB_SETCURSEL, 0, 0);
  2785.     gnSel = 0;
  2786.     DoDataExchange(hDlg, &glpavisi[0], FALSE);
  2787.  
  2788.     return (FALSE);
  2789. }
  2790. //
  2791. // *****************************************************************************
  2792. //
  2793. //      FUNCTION: InfoDlg_OnCommand(HWND, HWND, UINT)
  2794. //
  2795. //      PURPOSE:  Handles the child controls for dialog box
  2796. //
  2797. //      DIALOGBOX ID'S
  2798. //
  2799. //              IDOK            - ok button
  2800. //              IDCANCEL        - cancel button
  2801. //      IDC_STREAMS             - the dropdown listbox
  2802. //
  2803. static void InfoDlg_OnCommand(HWND hDlg, int control_source, HWND control_handle, UINT control_action )
  2804. {
  2805.  
  2806.     switch (control_source)
  2807.     {
  2808.     case IDC_STREAMS:
  2809.     {
  2810.         if (control_action != CBN_SELCHANGE)
  2811.         break;
  2812.         if (gnSel != CB_ERR)
  2813.         DoDataExchange (hDlg, &glpavisi[gnSel], TRUE);
  2814.         gnSel = (int)SendDlgItemMessage(hDlg, IDC_STREAMS, CB_GETCURSEL, 0, 0);
  2815.         if (gnSel != CB_ERR)
  2816.         DoDataExchange(hDlg, &glpavisi[gnSel], FALSE);
  2817.     }
  2818.     break;
  2819.     
  2820.     case IDOK:
  2821.     case IDCANCEL:
  2822.     {
  2823.         BOOL fOk = (control_source == IDOK);
  2824.         int i,ix;
  2825.     
  2826.         if (fOk)
  2827.         {
  2828.         ix = (int)SendDlgItemMessage(hDlg, IDC_STREAMS, CB_GETCURSEL, 0, 0);
  2829.         if (ix != CB_ERR)
  2830.             DoDataExchange(hDlg, &glpavisi[ix], TRUE);
  2831.         
  2832.         for (i = 0; i < gcpavi; ++i)
  2833.             EditStreamSetInfo(gapavi[i], &glpavisi[i], sizeof(AVISTREAMINFO));
  2834.         }
  2835.         GlobalFreePtr(glpavisi);
  2836.         EndDialog(hDlg, fOk);
  2837.     }
  2838.     break;
  2839.     
  2840.     }
  2841.     return;
  2842. }
  2843. //
  2844. // *****************************************************************************
  2845. //
  2846. //      FUNCTION: SetInfoNewDlgProc(HWND, UINT, WPARAM, LPARAM)
  2847. //
  2848. //      PURPOSE:  Processes messages for dialog box using message crackers
  2849. //
  2850. //      MESSAGES:
  2851. //
  2852. //              WM_INITDIALOG   - initialize dialog box
  2853. //              WM_COMMAND              - process user input
  2854. //
  2855. // *****************************************************************************
  2856. //
  2857. static LRESULT SetInfoNewDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
  2858. {
  2859.     switch (message)
  2860.     {
  2861.     HANDLE_MSG( hDlg, WM_INITDIALOG,            InfoDlg_OnInitDialog            );
  2862.     HANDLE_MSG( hDlg, WM_COMMAND,                       InfoDlg_OnCommand               );
  2863.     default:
  2864.         return Dlg_DefProc( hDlg, message, wParam, lParam );
  2865.     }
  2866. }
  2867. //
  2868. //
  2869. // *****************************************************************************
  2870. //
  2871. //      FUNCTION: SetInfoDlgProc(HWND, UINT, WPARAM, LPARAM)
  2872. //
  2873. //      PURPOSE:  Processes messages for info dialog box
  2874. //
  2875. //
  2876. BOOL CALLBACK SetInfoDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
  2877. {
  2878.     CheckDefDlgRecursion( &gfDefDlgEx );
  2879.     return SetDlgMsgResult( hDlg, message, SetInfoNewDlgProc( hDlg, message, wParam, lParam ) );
  2880. }
  2881.  
  2882.  
  2883. /*----------------------------------------------------------------------------*\
  2884. |    SetStreamInfo()                                                            |
  2885. |                                                                               |
  2886. |    Bring up the dialog to allow setting the stream info                       |
  2887. \*----------------------------------------------------------------------------*/
  2888.  
  2889. BOOL SetStreamInfo (HWND hwnd)
  2890. {
  2891.     return(DialogBox(ghInstApp, MAKEINTRESOURCE(IDD_STREAMINFO), hwnd, (DLGPROC)SetInfoDlgProc));
  2892. }
  2893.  
  2894.  
  2895.  
  2896. /*----------------------------------------------------------------------------*\
  2897. |    editPaste()                                                                |
  2898. |                                                                               |
  2899. |    PASTE the streams in this PFILE into our movie using the following logic:  |
  2900. |                                                                               |
  2901. |    Take a stream from the clipboard.  If you can find a similar type stream   |
  2902. |    in the app with a selection, paste it in before the selection.  If no      |
  2903. |    such stream exists, add it to the end.                                     |
  2904. \*----------------------------------------------------------------------------*/
  2905.  
  2906. void editPaste(HWND hwnd, PAVIFILE pfile)
  2907. {
  2908.     int         i, j, nVideo = 0, nAudio = 0, nStream;
  2909.     LONG        l;
  2910.     PAVISTREAM  pavi;
  2911.     AVISTREAMINFO avisClip, avis;
  2912.  
  2913.     FreeDrawStuff(hwnd);
  2914.  
  2915.     for (i=0; i<MAXNUMSTREAMS; i++) {
  2916.     if (AVIFileGetStream(pfile, &pavi, 0L, i) != AVIERR_OK)
  2917.         break;
  2918.     AVIStreamInfo(pavi, &avisClip, sizeof(avisClip));
  2919.     nStream = (avisClip.fccType == streamtypeVIDEO) ? nVideo : nAudio;
  2920.     for (j=nStream; j<gcpavi; j++) {
  2921.         AVIStreamInfo(gapavi[j], &avis, sizeof(avis));
  2922.         if (avis.fccType == avisClip.fccType && galSelStart[j] != -1) {
  2923.         l = AVIStreamLength(pavi);
  2924.         if (EditStreamPaste(gapavi[j], &galSelStart[j], &l,
  2925.                     pavi, AVIStreamStart(pavi), AVIStreamLength(pavi)) !=
  2926.             AVIERR_OK)
  2927.         {
  2928.             LoadString( ghInstApp, IDS_NOPASTE, gszBuffer, BUFSIZE );
  2929.             ErrMsg(gszBuffer);
  2930.         }
  2931.         galSelLen[j] = AVIStreamLength(pavi);
  2932.         break;
  2933.         }
  2934.     }
  2935.     if (j == gcpavi) {
  2936.         galSelStart[j] = AVIStreamStart(pavi);
  2937.         galSelLen[j] = AVIStreamLength(pavi);
  2938.         if (CreateEditableStream(&gapavi[j], pavi) != AVIERR_OK)
  2939.         {
  2940.         LoadString( ghInstApp, IDS_PASTEERROR, gszBuffer, BUFSIZE );
  2941.         ErrMsg(gszBuffer);
  2942.         AVIStreamRelease(pavi);
  2943.         break;
  2944.         }
  2945.         AVIStreamRelease(pavi);
  2946.         gcpavi++;
  2947.     }
  2948.     if (avisClip.fccType == streamtypeVIDEO)
  2949.         nVideo = ++j;
  2950.     else
  2951.         nAudio = ++j;
  2952.     }
  2953.     AVIFileRelease(pfile);
  2954.     InitStreams(hwnd);
  2955.     FixWindowTitle(hwnd);
  2956. }
  2957. /*----------------------------------------------------------------------------*\
  2958. |    MenuHandler()                                                              |
  2959. |                                                                               |
  2960. |    Process all of our Menu messages.                                  |
  2961. \*----------------------------------------------------------------------------*/
  2962. BOOL MenuHandler( HWND hwnd, int nMenuID )
  2963. {
  2964.     OPENFILENAME ofn;
  2965.  
  2966.     switch(nMenuID)
  2967.     {
  2968.     case MENU_ABOUT:
  2969.         //
  2970.         // Display an informative dialog box.
  2971.         //
  2972.         DialogBox(ghInstApp, MAKEINTRESOURCE(IDD_ABOUT), hwnd, (DLGPROC)AboutDlgProc);
  2973.         break;
  2974.         //
  2975.         // We want out of here!
  2976.         //
  2977.     case MENU_EXIT:
  2978.         PostMessage(hwnd,WM_CLOSE,0,0L);
  2979.         break;
  2980.     
  2981.         //
  2982.         // Set the compression options for each stream - pass an array of
  2983.         // streams and an array of compression options structures
  2984.         //
  2985.     case MENU_OPTIONS:
  2986.         AVISaveOptions(hwnd,
  2987.                ICMF_CHOOSE_KEYFRAME | ICMF_CHOOSE_DATARATE |
  2988.                ICMF_CHOOSE_PREVIEW,
  2989.                gcpavi, gapavi, galpAVIOptions);
  2990.         break;
  2991.     
  2992.         //
  2993.         // Save all the open streams into a file
  2994.         //
  2995.     case MENU_SAVEAS:
  2996.     
  2997.         gszSaveFileName[0] = 0;
  2998.     
  2999.         //
  3000.         // prompt user for file to save
  3001.         //
  3002.         ofn.lStructSize = sizeof(OPENFILENAME);
  3003.         ofn.hwndOwner = hwnd;
  3004.         ofn.hInstance = NULL;
  3005.         AVIBuildFilter(gszFilter, sizeof(gszFilter), TRUE);
  3006.         ofn.lpstrFilter = gszFilter;
  3007.         ofn.lpstrCustomFilter = NULL;
  3008.         ofn.nMaxCustFilter = 0;
  3009.         ofn.nFilterIndex = 0;
  3010.         ofn.lpstrFile = gszSaveFileName;
  3011.         ofn.nMaxFile = sizeof(gszSaveFileName);
  3012.         ofn.lpstrFileTitle = NULL;
  3013.         ofn.nMaxFileTitle = 0;
  3014.         ofn.lpstrInitialDir = NULL;
  3015.         LoadString( ghInstApp, IDS_SAVETITLE, gszBuffer, BUFSIZE );
  3016.         ofn.lpstrTitle = gszBuffer;
  3017.         ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY |
  3018.             OFN_OVERWRITEPROMPT;
  3019.         ofn.nFileOffset = 0;
  3020.         ofn.nFileExtension = 0;
  3021.         LoadString( ghInstApp, IDS_DEFEXT, gszBuffer, BUFSIZE );
  3022.         ofn.lpstrDefExt = gszBuffer;
  3023.         ofn.lCustData = 0;
  3024.         ofn.lpfnHook = NULL;
  3025.         ofn.lpTemplateName = NULL;
  3026.     
  3027.         //
  3028.         // If we get a filename, save it
  3029.         //
  3030.         if (GetSaveFileName(&ofn))
  3031.         {
  3032.             DWORD       fccHandler[MAXNUMSTREAMS];
  3033.             int         i;
  3034.             HRESULT     hr;
  3035.         
  3036.             StartWait();
  3037.         
  3038.             for (i = 0; i < gcpavi; i++)
  3039.             fccHandler[i] = galpAVIOptions[i]->fccHandler;
  3040.         
  3041.             hr = AVISaveV(gszSaveFileName,
  3042.                   NULL,
  3043.                   (AVISAVECALLBACK) SaveCallback,
  3044.                   gcpavi,
  3045.                   gapavi,
  3046.                   galpAVIOptions);
  3047.         if (hr != AVIERR_OK) {
  3048.             switch (hr) {
  3049.             case AVIERR_FILEOPEN:
  3050.             LoadString( ghInstApp, IDS_ERROVERWRITE, gszBuffer, BUFSIZE );
  3051.             ErrMsg(gszBuffer);
  3052.             break;
  3053.             default:
  3054.             LoadString( ghInstApp, IDS_SAVEERROR, gszBuffer, BUFSIZE );
  3055.             ErrMsg(gszBuffer);
  3056.             }
  3057.         }
  3058.             // Now put the video compressors back that we stole
  3059.             for (i = 0; i < gcpavi; i++)
  3060.             galpAVIOptions[i]->fccHandler = fccHandler[i];
  3061.         
  3062.             EndWait();
  3063.             FixWindowTitle(hwnd);
  3064.         }
  3065.         break;
  3066.     
  3067.         //
  3068.         // Close everything
  3069.         //
  3070.     case MENU_CLOSE:
  3071.         FreeAvi(hwnd);
  3072.         gszFileName[0] = '\0';
  3073.         FixWindowTitle(hwnd);
  3074.         break;
  3075.     
  3076.         //
  3077.         // Open a new file, or merge streams with a new file
  3078.         //
  3079.     case MENU_OPEN:
  3080.     case MENU_MERGE:
  3081.         gszFileName[0] = 0;
  3082.     
  3083.         //
  3084.         // prompt user for file to open
  3085.         //
  3086.         ofn.lStructSize = sizeof(OPENFILENAME);
  3087.         ofn.hwndOwner = hwnd;
  3088.         ofn.hInstance = NULL;
  3089.         if (nMenuID == MENU_MERGE)
  3090.         LoadString( ghInstApp, IDS_MERGETITLE, gszBuffer, BUFSIZE );
  3091.         else
  3092.         LoadString( ghInstApp, IDS_OPENTITLE, gszBuffer, BUFSIZE );
  3093.         ofn.lpstrTitle = gszBuffer;
  3094.         AVIBuildFilter(gszFilter, sizeof(gszFilter), FALSE);
  3095.         ofn.lpstrFilter = gszFilter;
  3096.         ofn.lpstrCustomFilter = NULL;
  3097.         ofn.nMaxCustFilter = 0;
  3098.         ofn.nFilterIndex = 0;
  3099.         ofn.lpstrFile = gszFileName;
  3100.         ofn.nMaxFile = sizeof(gszFileName);
  3101.         ofn.lpstrFileTitle = NULL;
  3102.         ofn.nMaxFileTitle = 0;
  3103.         ofn.lpstrInitialDir = NULL;
  3104.         ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST |OFN_HIDEREADONLY;
  3105.         ofn.nFileOffset = 0;
  3106.         ofn.nFileExtension = 0;
  3107.         ofn.lpstrDefExt = NULL;
  3108.         ofn.lCustData = 0;
  3109.         ofn.lpfnHook = NULL;
  3110.         ofn.lpTemplateName = NULL;
  3111.     
  3112.         //
  3113.         // If we've got a filename, go open it
  3114.         //
  3115.         if (GetOpenFileNamePreview(&ofn))
  3116.         InitAvi(hwnd, gszFileName, nMenuID);
  3117.     
  3118.         break;
  3119.     
  3120.     case MENU_ZOOMQUARTER:
  3121.         gwZoom = 1;
  3122.         FixScrollbars(hwnd);
  3123.         InvalidateRect(hwnd, NULL, TRUE);
  3124.         break;
  3125.     
  3126.     case MENU_ZOOMHALF:
  3127.         gwZoom = 2;
  3128.         FixScrollbars(hwnd);
  3129.         InvalidateRect(hwnd, NULL, TRUE);
  3130.         break;
  3131.     
  3132.     case MENU_ZOOM1:
  3133.         gwZoom = 4;
  3134.         FixScrollbars(hwnd);
  3135.         InvalidateRect(hwnd, NULL, TRUE);
  3136.         break;
  3137.     
  3138.     case MENU_ZOOM2:
  3139.         gwZoom = 8;
  3140.         FixScrollbars(hwnd);
  3141.         InvalidateRect(hwnd, NULL, TRUE);
  3142.         break;
  3143.     
  3144.     case MENU_ZOOM4:
  3145.         gwZoom = 16;
  3146.         FixScrollbars(hwnd);
  3147.         InvalidateRect(hwnd, NULL, TRUE);
  3148.         break;
  3149.     
  3150.     //
  3151.     // Simulate playing the file.  We just play the 1st audio stream and let
  3152.     // our main message loop scroll the video by whenever it's bored.
  3153.     //
  3154.     case MENU_PREVIEW:
  3155.         if (gfAudioFound)
  3156.         aviaudioPlay(hwnd,
  3157.                  gapavi[giFirstAudio],
  3158.                  AVIStreamTimeToSample(gapavi[giFirstAudio], GetScrollTime(hwnd)),
  3159.                  AVIStreamEnd(gapavi[giFirstAudio]),
  3160.                  FALSE);
  3161.         gfPlaying = TRUE;
  3162.         glPlayStartTime = timeGetTime();
  3163.         glPlayStartPos = GetScrollTime(hwnd);
  3164.         break;
  3165.     
  3166.         //
  3167.         // Stop the play preview
  3168.         //
  3169.     case MENU_STOP:
  3170.         if (gfAudioFound)
  3171.         aviaudioStop();
  3172.         gfPlaying = FALSE;
  3173.         break;
  3174.     
  3175.     case MENU_SETINFO:
  3176.         if (SetStreamInfo(hwnd))
  3177.         {
  3178.         FreeDrawStuff(hwnd);        // !!! in order to call InitStreams
  3179.         InitStreams(hwnd);          // !!! Nukes COMP options
  3180.         InvalidateRect(hwnd, NULL, TRUE);
  3181.         }
  3182.         break;
  3183.     
  3184.     case MENU_CUT:
  3185.     case MENU_COPY:
  3186.     case MENU_DELETE:
  3187.     {
  3188.         PAVIFILE    pf;
  3189.         int         i;
  3190.     
  3191.         //
  3192.         // Walk our list of selections and make streams out of each section
  3193.         //
  3194.         gcpaviSel = 0;
  3195.         for (i = 0; i < gcpavi; i++) {
  3196.         if (galSelStart[i] != -1) {
  3197.             // !!! What if the start and length change?
  3198.             if (nMenuID == MENU_COPY) {
  3199.             if (EditStreamCopy(gapavi[i], &galSelStart[i],
  3200.                        &galSelLen[i], &gapaviSel[gcpaviSel++]) != 0) {
  3201.                 --gcpaviSel;
  3202.                 LoadString( ghInstApp, IDS_STRCPYERR, gszBuffer, BUFSIZE );
  3203.                 ErrMsg(gszBuffer);
  3204.             }
  3205.             } else {
  3206.             if (EditStreamCut(gapavi[i], &galSelStart[i],
  3207.                       &galSelLen[i], &gapaviSel[gcpaviSel++]) != 0) {
  3208.                 --gcpaviSel;
  3209.                 LoadString( ghInstApp, IDS_STRCUTERR, gszBuffer, BUFSIZE );
  3210.                 ErrMsg(gszBuffer);
  3211.             }
  3212.             }
  3213.         }
  3214.         }
  3215.     
  3216.     
  3217.         for (i = gcpavi - 1; i >= 0; i--) {
  3218.         // Check to see if any stream is entirely gone now....
  3219.         if (AVIStreamLength(gapavi[i]) == 0) {
  3220.             NukeAVIStream(i);
  3221.         }
  3222.         }
  3223.     
  3224.         //
  3225.         // Put the selected stuff up on the clipboard
  3226.         //
  3227.         if (gcpaviSel && nMenuID != MENU_DELETE) {
  3228.         PAVISTREAM          gapaviTemp[MAXNUMSTREAMS];
  3229.         int i;
  3230.         
  3231.         //
  3232.         // Clone the edited streams, so that if the user does
  3233.         // more editing, the thing on the clipboard won't
  3234.         // suddenly change....
  3235.         //
  3236.         for (i = 0; i < gcpaviSel; i++) {
  3237.             gapaviTemp[i] = NULL;
  3238.             // !!! error check
  3239.             EditStreamClone(gapaviSel[i], &gapaviTemp[i]);
  3240.         }
  3241.         
  3242.         AVIMakeFileFromStreams(&pf, gcpaviSel, gapaviTemp);
  3243.         if (AVIPutFileOnClipboard(pf) != AVIERR_OK)
  3244.         {
  3245.             LoadString( ghInstApp, IDS_NOCLIP, gszBuffer, BUFSIZE );
  3246.             ErrMsg(gszBuffer);
  3247.         }
  3248.         for (i = 0; i < gcpaviSel; i++) {
  3249.             AVIStreamRelease(gapaviTemp[i]);
  3250.         }
  3251.         
  3252.         AVIFileRelease(pf);
  3253.         }
  3254.     
  3255.         for (i = 0; i < gcpaviSel; i++)
  3256.         AVIStreamRelease(gapaviSel[i]);
  3257.     
  3258.         //
  3259.         // If we cut out the selections, then they don't exist anymore.
  3260.         //
  3261.         if (gcpaviSel && (nMenuID == MENU_DELETE || nMenuID == MENU_CUT)) {
  3262.         SelectStream(hwnd, -1, -1, -1, FALSE, FALSE);
  3263.         }
  3264.     
  3265.         //
  3266.         // We just changed the world!
  3267.         //
  3268.         FreeDrawStuff(hwnd);        // !!! in order to call InitStreams
  3269.         InitStreams(hwnd);          // !!! Nukes COMP options
  3270.         InvalidateRect(hwnd, NULL, TRUE);
  3271.     
  3272.         break;
  3273.     }
  3274.     
  3275.     case MENU_PASTE:
  3276.     {
  3277.         PAVIFILE pf;
  3278.     
  3279.         AVIGetFromClipboard(&pf);
  3280.     
  3281.         if (pf) {
  3282.         editPaste(hwnd, pf);
  3283.         }
  3284.         break;
  3285.     }
  3286.     case MENU_NAME:
  3287.         if (ghwndEdit == NULL)
  3288.         EditStreamName(hwnd);
  3289.         else
  3290.         EditDone(hwnd, FALSE);
  3291.     
  3292.     
  3293.         break;
  3294.     }
  3295.     return TRUE;
  3296. }
  3297. /*-----------------------------------------------------------------------------
  3298.  * FrameVideo()
  3299.  *
  3300.  * Puts a border around a video frame, the size of a selection
  3301.  */
  3302.  
  3303. void FrameVideo(HDC hdc, RECT *rcFrame, HBRUSH hbr)
  3304. {
  3305.    RECT rcTop,rcBottom,rcLeft,rcRight;
  3306.    HBRUSH hbrOld;
  3307.  
  3308.    // Calculate 4 rectangles, which 'frame' rcFrame.
  3309.    rcTop.left = rcFrame->left - HSPACE/2;
  3310.    rcTop.top = rcFrame->top - SELECTVSPACE;
  3311.    rcTop.right = rcFrame->right + HSPACE/2;
  3312.    rcTop.bottom = rcFrame->top;
  3313.    rcLeft.left = rcFrame->left - HSPACE/2;
  3314.    rcLeft.top = rcFrame->top - SELECTVSPACE;
  3315.    rcLeft.right = rcFrame->left;
  3316.    rcLeft.bottom = rcFrame->bottom + SELECTVSPACE;
  3317.    rcRight.left = rcFrame->right;
  3318.    rcRight.top = rcFrame->top - SELECTVSPACE;
  3319.    rcRight.right = rcFrame->right + HSPACE/2;
  3320.    rcRight.bottom = rcFrame->bottom + SELECTVSPACE;
  3321.    rcBottom.left = rcFrame->left - HSPACE/2;
  3322.    rcBottom.top = rcFrame->bottom;
  3323.    rcBottom.right = rcFrame->right + HSPACE/2;
  3324.    rcBottom.bottom = rcFrame->bottom + SELECTVSPACE;
  3325.  
  3326.    // Now put each rectangle on screen
  3327.    hbrOld = SelectObject(hdc, hbr);
  3328.    PatBlt(hdc, rcTop.left, rcTop.top, rcTop.right - rcTop.left, rcTop.bottom - rcTop.top, PATCOPY);
  3329.    PatBlt(hdc, rcLeft.left, rcLeft.top, rcLeft.right - rcLeft.left, rcLeft.bottom - rcLeft.top, PATCOPY);
  3330.    PatBlt(hdc, rcRight.left, rcRight.top, rcRight.right - rcRight.left, rcRight.bottom - rcRight.top, PATCOPY);
  3331.    PatBlt(hdc, rcBottom.left, rcBottom.top, rcBottom.right - rcBottom.left, rcBottom.bottom - rcBottom.top, PATCOPY);
  3332.    SelectObject(hdc, hbrOld);
  3333.  
  3334. }
  3335.  
  3336. /*----------------------------------------------------------------------------*\
  3337. |   ErrMsg()                                                                    |
  3338. |                                                                               |
  3339. |   Opens a Message box with a error message in it.  The user can               |
  3340. |   select the OK button to continue                                            |
  3341. \*----------------------------------------------------------------------------*/
  3342.  
  3343. int ErrMsg (LPSTR sz,...)
  3344. {
  3345.     static char szOutput[4*BUFSIZE];
  3346.  
  3347.     va_list va;
  3348.  
  3349.     va_start(va, sz);
  3350.     wvsprintf (szOutput,sz,va);      /* Format the string */
  3351.     va_end(va);
  3352.     MessageBox(NULL,szOutput,NULL, MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL);
  3353.     return FALSE;
  3354. }
  3355.  
  3356.  
  3357. /* AboutDlgProc()
  3358.  *
  3359.  * Dialog Procedure for the "about" dialog box.
  3360.  *
  3361.  */
  3362.  
  3363. BOOL CALLBACK AboutDlgProc(
  3364.                   HWND  hwnd,
  3365.                   UINT  msg,
  3366.                   WPARAM        wParam,
  3367.                   LPARAM        lParam)
  3368. {
  3369.     switch (msg) {
  3370.     case WM_COMMAND:
  3371.         EndDialog(hwnd, TRUE);
  3372.         return TRUE;
  3373.     case WM_INITDIALOG:
  3374.         return TRUE;
  3375.     }
  3376.     return FALSE;
  3377. }
  3378.