AVIEDIT.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:100k
源码类别:

Windows编程

开发平台:

Visual C++

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