home *** CD-ROM | disk | FTP | other *** search
/ Cutting-Edge 3D Game Programming with C++ / CE3DC++.ISO / TOOLS / MIDIPLYR / MAINWND.C < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-02  |  31.7 KB  |  1,016 lines

  1. /*****************************************************************************
  2. *
  3. *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  4. *  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
  5. *  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR
  6. *  A PARTICULAR PURPOSE.
  7. *
  8. *  Copyright (C) 1993 - 1995 Microsoft Corporation. All Rights Reserved.
  9. *
  10. ******************************************************************************
  11. *
  12. * MainWnd.C
  13. *
  14. * Message handlers and UI for the main window and associated controls
  15. *
  16. *****************************************************************************/
  17.  
  18. #pragma warning(disable:4756)
  19.  
  20. #define _INC_SHELLAPI
  21. #include <windows.h>
  22. #undef _INC_SHELLAPI
  23.  
  24. #include <shellapi.h>
  25. #include <windowsx.h>
  26. #include <mmsystem.h>
  27. #include <commdlg.h>
  28. #include <commctrl.h>
  29. #include <ctype.h>
  30.  
  31. #include "debug.h"
  32.  
  33. #include "MIDIPlyr.H"
  34.  
  35. #define BITMAP_COUNT    6           /* Number of buttons in bitmap */
  36.  
  37. PRIVATE HWND            ghWndToolbar                    = NULL;
  38. PRIVATE HWND            ghWndStatus                     = NULL;
  39. PRIVATE HWND            ghWndTime                       = NULL;
  40. PRIVATE char            gszAppTitle[80]                 = "";
  41. PRIVATE int             gnSB_TFPaneSize                 = 0;
  42. PRIVATE char            gszOpenName[MAX_FILEPATH]       = "";
  43. PRIVATE char            gszOpenTitle[MAX_FILEPATH]      = "";
  44. PRIVATE char BCODE      gszFilter[]                      =
  45.     "MIDI File (*.MID;*.RMI)\0*.MID;*.RMI\0"
  46.     "All Files (*.*)\0*.*\0";
  47.  
  48. PRIVATE char BCODE      gszDefExtension[]               = "MID";
  49. PRIVATE BOOL            gbAutoPlay                      = TRUE;
  50. PRIVATE UINT            guDevice                        = 0;
  51.  
  52. // the BYTE bReserved[2] member is present only in WIN32
  53. #ifdef _WIN32
  54. #define BRESERVED00 0, 0,
  55. #else
  56. #define BRESERVED00
  57. #endif
  58.  
  59. PRIVATE TBBUTTON gatbButton[] =
  60. {
  61.     {0, IDM_OPEN,       TBSTATE_ENABLED, TBSTYLE_BUTTON, BRESERVED00 0, -1},
  62.     {0, -1,             TBSTATE_ENABLED, TBSTYLE_SEP,    BRESERVED00 0, -1},
  63.     {1, IDM_REWIND,     0,               TBSTYLE_BUTTON, BRESERVED00 0, -1},
  64.     {2, IDM_PLAY,       0,               TBSTYLE_BUTTON, BRESERVED00 0, -1},
  65.     {3, IDM_STOP,       0,               TBSTYLE_BUTTON, BRESERVED00 0, -1},
  66.     {4, IDM_PAUSE,      0,               TBSTYLE_BUTTON, BRESERVED00 0, -1},
  67.     {5, IDM_FASTFWD,    0,               TBSTYLE_BUTTON, BRESERVED00 0, -1},
  68. };
  69.  
  70. #define BUTTON_COUNT (sizeof(gatbButton)/sizeof(gatbButton[0]))
  71.  
  72. PRIVATE VOID FNLOCAL InitToolbar(HWND hWnd);
  73. PRIVATE VOID FNLOCAL InitStatusBar(HWND hWnd);
  74. PRIVATE VOID FNLOCAL ResizeStatusBar(HWND hWnd);
  75. PRIVATE VOID FNLOCAL SyncUI(HWND hWnd);
  76. PRIVATE VOID FNLOCAL SetOneAction(HWND hWnd, int nMenuID, BOOL fEnable);
  77. PRIVATE VOID FNLOCAL AttemptFileOpen(HWND hWnd);
  78. PRIVATE BOOL FNLOCAL PrerollAndWait(HWND hWnd);
  79.  
  80. PRIVATE BOOL FNLOCAL MWnd_OnCreate(HWND hWnd, CREATESTRUCT FAR* lpCreateStruct);
  81. PRIVATE VOID FNLOCAL MWnd_OnGetMinMaxInfo(HWND hWnd, MINMAXINFO FAR* lpMinMaxInfo);
  82. PRIVATE VOID FNLOCAL MWnd_OnSize(HWND hWnd, UINT state, int cx, int cy);
  83. PRIVATE VOID FNLOCAL MWnd_OnPaint(HWND hWnd);
  84. PRIVATE VOID FNLOCAL MWnd_OnDropFiles(HWND hWnd, HDROP hDrop);
  85. PRIVATE VOID FNLOCAL MWnd_OnFileOpen(HWND hWnd);
  86. PRIVATE VOID FNLOCAL MWnd_OnCommandToggleChild(HWND hWnd, UINT id);
  87. PRIVATE VOID FNLOCAL MWnd_OnCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify);
  88. PRIVATE VOID FNLOCAL MWnd_OnDestroy(HWND hWnd);
  89.  
  90. /*****************************************************************************
  91. *
  92. * InitToolbar
  93. *
  94. * Called to create the toolbar
  95. *
  96. * HWND hWnd                 - Application window which toolbar is a child of
  97. *
  98. * Create and show the toolbar window.
  99. *
  100. *****************************************************************************/
  101. PRIVATE VOID FNLOCAL InitToolbar(
  102.     HWND                    hWnd)
  103. {
  104.     ghWndToolbar = CreateToolbarEx(hWnd,
  105.                                    WS_CHILD|WS_CLIPSIBLINGS|WS_CLIPCHILDREN,
  106.                                    IDC_TOOLBAR,
  107.                                    BITMAP_COUNT,
  108.                                    ghInst,
  109.                                    IDB_TOOLBAR,
  110.                                    gatbButton,
  111.                                    BUTTON_COUNT,
  112.                                    0,  0,
  113.                                    0,  0,
  114.                                    sizeof(TBBUTTON));
  115.  
  116.     if (ghWndToolbar)
  117.         ShowWindow(ghWndToolbar, SW_RESTORE);
  118. }
  119.  
  120. /*****************************************************************************
  121. *
  122. * InitStatusBar
  123. *
  124. * Called to create the status bar
  125. *
  126. * HWND hWnd                 - Application window which status bar is a child of
  127. *
  128. * Create and show the status window.
  129. *
  130. *****************************************************************************/
  131. PRIVATE VOID FNLOCAL InitStatusBar(
  132.     HWND                    hWnd)
  133. {
  134.     HWND                    hWndDesktop;
  135.     HFONT                   hFontStat;
  136.     HDC                     hDC;
  137.     UINT                    idx;
  138.     SIZE                    size;
  139.  
  140.     ghWndStatus = CreateStatusWindow(WS_CHILD|SBARS_SIZEGRIP,
  141.                                      "",
  142.                                      hWnd,
  143.                                      IDC_STATBAR);
  144.  
  145.     if (ghWndStatus)
  146.     {
  147.         hWndDesktop = GetDesktopWindow();
  148.         hFontStat = (HFONT)SendMessage(ghWndStatus, WM_GETFONT, 0, 0L);
  149.         hDC = GetDC(hWndDesktop);
  150.  
  151.         if (hFontStat != (HFONT)NULL && hDC != (HDC)NULL)
  152.         {
  153.             hFontStat = (HFONT)SelectObject(hDC, hFontStat);
  154.  
  155.             gnSB_TFPaneSize = 0;
  156.             for (idx = 0; idx < N_TIME_FORMATS; idx++)
  157.             {
  158.                 GetTextExtentPoint(hDC,
  159.                                    grgszTimeFormats[idx],
  160.                                    lstrlen(grgszTimeFormats[idx]),
  161.                                    &size);
  162.  
  163.                 gnSB_TFPaneSize = max(gnSB_TFPaneSize, size.cx);
  164.             }
  165.  
  166.             SelectObject(hDC, hFontStat);
  167.  
  168.             gnSB_TFPaneSize *= 2;
  169.         }
  170.  
  171.         if (hDC != (HDC)NULL)
  172.             ReleaseDC(hWndDesktop, hDC);
  173.  
  174.         ResizeStatusBar(hWnd);
  175.         
  176.         FORWARD_WM_COMMAND(hWnd, gnTimeFormat, 0, 0, SendMessage);
  177.         ShowWindow(ghWndStatus, SW_RESTORE);
  178.     }
  179. }
  180.  
  181. /*****************************************************************************
  182. *
  183. * ResizeStatusBar
  184. *
  185. * Force the status bar to resize to fit in the main window
  186. *
  187. * HWND hWnd                 - Application window which status bar is a child of
  188. *
  189. * Figure out the pane sizes and send a message to the status bar to set them.
  190. *
  191. *****************************************************************************/
  192. PRIVATE VOID FNLOCAL ResizeStatusBar(
  193.     HWND                    hWnd)
  194. {
  195.     RECT                    rc;
  196.     int                     rnPaneEdge[SB_N_PANES];
  197.  
  198.     GetClientRect(hWnd, &rc);
  199.  
  200.     /* SB_SETPARTS expects:
  201.     **  wParam == Number of panes in status bar.
  202.     **  lParam == Pointer to an array of int's indicating the right-hand
  203.     **            coordinate of each pane.
  204.     */
  205.     rnPaneEdge[SB_PANE_STATE] = rc.right - gnSB_TFPaneSize;
  206.     rnPaneEdge[SB_PANE_TFMT]  = -1;
  207.  
  208.     SendMessage(ghWndStatus,
  209.                 SB_SETPARTS,
  210.                 SB_N_PANES,
  211.                 (DWORD)(LPINT)(rnPaneEdge));
  212. }
  213.  
  214. /*****************************************************************************
  215. *
  216. * SyncUI
  217. *
  218. * Bring all UI elements into sync with the state of the sequencer
  219. *
  220. * HWND hWnd                 - Application main window 
  221. *
  222. * Build a flag word of the actions which are allowed from the current state.
  223. * Set the menu items and toolbar buttons for each action appropriately.
  224. * Show the current state as a string in the status bar.
  225. * Depress the pause button if the sequencer is paused.
  226. * Cause the time window to update.
  227. *
  228. *****************************************************************************/
  229. #define SUI_F_CANREWIND     0x0001
  230. #define SUI_F_CANPLAY       0x0002
  231. #define SUI_F_CANPAUSE      0x0004
  232. #define SUI_F_CANSTOP       0x0008
  233. #define SUI_F_CANFASTFWD    0x0010
  234. #define SUI_F_CANSELDEVICE  0x0020
  235.  
  236. PRIVATE VOID FNLOCAL SyncUI(
  237.     HWND                    hWnd)
  238. {
  239.     WORD                    wActionFlags;
  240.     TICKS                   ticks;
  241.     UINT                    uState;
  242.     char                    szState[40];
  243.     BOOL                    fPress;
  244.  
  245.     wActionFlags = 0;
  246.     uState = SEQ_S_NOFILE;
  247.     
  248.     if (gpSeq != NULL)
  249.     {
  250.         uState = gpSeq->uState;
  251.         switch (uState)
  252.         {
  253.         case SEQ_S_NOFILE:
  254.             wActionFlags = SUI_F_CANSELDEVICE;
  255.             break;
  256.  
  257.         case SEQ_S_OPENED:
  258.         case SEQ_S_PREROLLED:
  259.             wActionFlags = SUI_F_CANPLAY|SUI_F_CANREWIND|SUI_F_CANFASTFWD|SUI_F_CANSELDEVICE;
  260.             break;
  261.  
  262.         case SEQ_S_PAUSED:
  263.         case SEQ_S_PLAYING:
  264.             wActionFlags = SUI_F_CANPAUSE|SUI_F_CANSTOP|SUI_F_CANREWIND|SUI_F_CANFASTFWD;
  265.             break;
  266.  
  267.  
  268.         case SEQ_S_PREROLLING:
  269.         case SEQ_S_STOPPING:
  270.             assert(0);
  271.             wActionFlags = 0;
  272.             break;
  273.         }
  274.  
  275.         /* If we allow ff/rewind, check for boundry conditions --
  276.         **  If already at start, can't rewind
  277.         **  If already at end, can't fast forward
  278.         */
  279.         if (wActionFlags & SUI_F_CANREWIND)
  280.         {
  281.             seqTime(gpSeq, &ticks);
  282.             if (ticks == 0)
  283.                 wActionFlags &= ~SUI_F_CANREWIND;
  284.             else if (ticks >= gpSeq->tkLength)
  285.                 wActionFlags &= ~SUI_F_CANFASTFWD;
  286.         }
  287.     }
  288.     
  289.     SetOneAction(hWnd, IDM_REWIND, wActionFlags & SUI_F_CANREWIND);
  290.     SetOneAction(hWnd, IDM_PLAY,   wActionFlags & SUI_F_CANPLAY);
  291.     SetOneAction(hWnd, IDM_PAUSE,  wActionFlags & SUI_F_CANPAUSE);
  292.     SetOneAction(hWnd, IDM_STOP,   wActionFlags & SUI_F_CANSTOP);
  293.     SetOneAction(hWnd, IDM_FASTFWD,wActionFlags & SUI_F_CANFASTFWD);
  294.  
  295.     EnableMenuItem(GetMenu(hWnd),
  296.                    POS_PLAYTHRU,
  297.                    MF_BYPOSITION|((wActionFlags & SUI_F_CANSELDEVICE) ? MF_ENABLED : MF_DISABLED));
  298.  
  299.     DrawMenuBar(hWnd);
  300.  
  301.     szState[0] = '\0';
  302.     LoadString(ghInst, IDS_STATES + uState, szState, sizeof(szState));
  303.     SendMessage(ghWndStatus, SB_SETTEXT, SB_PANE_STATE, (LPARAM)(LPSTR)szState);
  304.  
  305.     fPress = (gpSeq->uState == SEQ_S_PAUSED);
  306.     SendMessage(ghWndToolbar,
  307.                 TB_PRESSBUTTON,
  308.                 IDM_PAUSE,
  309.                 fPress);
  310.  
  311.     InvalidateRect(ghWndTime, NULL, TRUE);
  312. }
  313.  
  314. /*****************************************************************************
  315. *
  316. * SetOneAction
  317. *
  318. * Update the state of one action in both the toolbar and action menu
  319. *
  320. * HWND hWnd                 - Application main window
  321. * int nMenuID               - Menu ID of action
  322. * BOOL fEnable              - Enable or disable this action
  323. *
  324. *****************************************************************************/
  325. PRIVATE VOID FNLOCAL SetOneAction(
  326.     HWND                hWnd,
  327.     int                 nMenuID,
  328.     BOOL                fEnable)
  329. {
  330.     EnableMenuItem(GetMenu(hWnd),
  331.                    nMenuID,
  332.                    MF_BYCOMMAND|(fEnable ? MF_ENABLED : MF_DISABLED));
  333.  
  334.     SendMessage(ghWndToolbar,
  335.                 TB_ENABLEBUTTON,
  336.                 nMenuID,
  337.                 (DWORD)fEnable);
  338. }
  339.  
  340. /*****************************************************************************
  341. *
  342. * AttemptFileOpen
  343. *
  344. * Try to open the given file in the sequencer.
  345. *
  346. * HWND hWnd                 - Application main window
  347. *
  348. * Stop and close the current file.
  349. * Open the new file.
  350. * Preroll the new file.
  351. * Set the title test for the main window.
  352. * Call SyncUI to update available actions.
  353. *
  354. *****************************************************************************/
  355. PRIVATE VOID FNLOCAL AttemptFileOpen(
  356.     HWND                    hWnd)
  357. {
  358.     MMRESULT                mmrc;
  359.     PREROLL                 preroll;
  360.     PSTR                    pStrFile    = gszUntitled;
  361.     
  362.     /* Stop, close, etc. if we're still playing
  363.     */
  364.     DPF(1, "Calling seqStop(); state = %u", gpSeq->uState);
  365.     
  366.     if (MMSYSERR_NOERROR == seqStop(gpSeq))
  367.     {
  368.         DPF(1, "Waiting for SEQ_F_WAITING to clear...");
  369.         
  370.         while (gpSeq->fdwSeq & SEQ_F_WAITING)
  371.             MessagePump();
  372.     }
  373.  
  374.     DPF(1, "Calling seqCloseFile(); state = %u", gpSeq->uState);
  375.  
  376.     seqCloseFile(gpSeq);
  377.  
  378.     DPF(1, "Calling seqOpenFile(); state = %u", gpSeq->uState);
  379.     /* Open new file
  380.     */
  381.     gpSeq->pstrFile = gszOpenName;
  382.     mmrc = seqOpenFile(gpSeq);
  383.     if (mmrc != MMSYSERR_NOERROR)
  384.     {
  385.         Error(hWnd, IDS_OPENFAILED, mmrc);
  386.         return;
  387.     }
  388.  
  389.     preroll.tkBase = 0;
  390.     preroll.tkEnd  = gpSeq->tkLength;
  391.  
  392.     if (PrerollAndWait(hWnd))
  393.         pStrFile = gszOpenTitle;
  394.  
  395.     wsprintf(gszAppTitle, gszAppTitleMask, (LPSTR)pStrFile);
  396.     SetWindowText(hWnd, gszAppTitle);
  397.  
  398.     SyncUI(hWnd);
  399. }
  400.  
  401. /*****************************************************************************
  402. *
  403. * PrerollAndWait
  404. *
  405. * Prerolls the sequencer using the current device ID and file.
  406. *
  407. * HWND hWnd                 - Current window
  408. *
  409. * Just call preroll and loop processing messages until done.
  410. *
  411. *****************************************************************************/
  412. PRIVATE BOOL FNLOCAL PrerollAndWait(
  413.     HWND                    hWnd)
  414. {
  415.     PREROLL                 preroll;
  416.     MMRESULT                mmrc;
  417.     
  418.     preroll.tkBase = 0;
  419.     preroll.tkEnd  = gpSeq->tkLength;
  420.  
  421.     gpSeq->uDeviceID = guDevice;
  422.  
  423.     DPF(1, "Call seqPreroll");
  424.     if (MMSYSERR_NOERROR == (mmrc = seqPreroll(gpSeq, &preroll)))
  425.     {
  426.         DPF(1, "Begin preroll wait...");
  427.         while (gpSeq->fdwSeq & SEQ_F_WAITING)
  428.             MessagePump();
  429.         DPF(1, "End preroll wait...");
  430.  
  431.         if (gpSeq->mmrcLastErr != MMSYSERR_NOERROR)
  432.         {
  433.             DPF(1, "Preroll completion %lu", (DWORD)gpSeq->mmrcLastErr);
  434.             
  435.             Error(hWnd, IDS_PREROLLFAILED, gpSeq->mmrcLastErr);
  436.             return FALSE;
  437.         }
  438.  
  439.         return TRUE;
  440.     }
  441.     else
  442.     {
  443.         Error(hWnd, IDS_PREROLLFAILED, mmrc);
  444.         seqCloseFile(gpSeq);
  445.  
  446.         return FALSE;
  447.     }
  448. }
  449.  
  450. /*****************************************************************************
  451. *
  452. * MWnd_OnCreate
  453. *
  454. * Handle WM_CREATE message to main application window.
  455. *
  456. * HWND hWnd                 - Window handle
  457. * CREATESTRUCT FAR* lpCreateStruct
  458. *                           - Pointer to creation parameters for the window.
  459. *
  460. * Returns TRUE on success. Returning FALSE will cause the window to be
  461. * destroyed and the application will exit.
  462. *
  463. * Set the default time format.
  464. * Create the tool and status bars.
  465. * Create the time window as a child of the main app window and show it.
  466. * Set the main window's title to show no document ('Untitled').
  467. * Accept drag/drop files.
  468. * Call SyncUI to update the enable status of the toolbar and menu items.
  469. *
  470. *****************************************************************************/
  471. PRIVATE BOOL FNLOCAL MWnd_OnCreate(
  472.     HWND                    hWnd,
  473.     CREATESTRUCT FAR*       lpCreateStruct)
  474. {
  475.     HMENU                   hMenu;
  476.     HMENU                   hMenuOptions;
  477.     HMENU                   hMenuPlayThru;
  478.     UINT                    cDevs;
  479.     UINT                    idx;
  480.     RECT                    rc;
  481.     MIDIOUTCAPS             moutCaps;
  482.  
  483.     gnTimeFormat = IDS_TF_FIRST;
  484.  
  485.     InitToolbar(hWnd);
  486.     InitStatusBar(hWnd);
  487.     
  488.     hMenu = GetMenu(hWnd);
  489.     hMenuOptions = GetSubMenu(hMenu, POS_OPTIONS);
  490.     hMenuPlayThru = GetSubMenu(hMenu, POS_PLAYTHRU);
  491.  
  492.     AppendMenu(hMenuOptions, MF_SEPARATOR, 0, NULL);
  493.     
  494.     for (idx = 0; idx < N_TIME_FORMATS; idx++)
  495.     {
  496.         AppendMenu(hMenuOptions,
  497.                    MF_ENABLED|MF_STRING,
  498.                    IDS_TF_FIRST + idx,
  499.                    grgszTimeFormats[idx]);
  500.     }
  501.  
  502.     cDevs = midiOutGetNumDevs();
  503.     if (cDevs)
  504.         AppendMenu(hMenuPlayThru, MF_SEPARATOR, 0, NULL);
  505.     
  506.     for (idx = 0; idx < cDevs; idx++)
  507.     {
  508.         if (midiOutGetDevCaps(idx, &moutCaps, sizeof(moutCaps)) == MMSYSERR_NOERROR)
  509.         {
  510.             AppendMenu(hMenuPlayThru,
  511.                        MF_ENABLED|MF_STRING,
  512.                        IDM_DEVICES + idx,
  513.                        moutCaps.szPname);
  514.         }
  515.     }
  516.     
  517.     CheckMenuItem(hMenu, IDM_TOOLBAR, MF_BYCOMMAND|MF_CHECKED);
  518.     CheckMenuItem(hMenu, IDM_STATUS,  MF_BYCOMMAND|MF_CHECKED);
  519.     CheckMenuItem(hMenu, IDM_AUTOPLAY,MF_BYCOMMAND|MF_CHECKED);
  520.     CheckMenuItem(hMenu, gnTimeFormat,MF_BYCOMMAND|MF_CHECKED);
  521.     CheckMenuItem(hMenu, IDM_DEVICES,  MF_BYCOMMAND|MF_CHECKED);  
  522.  
  523.     GetClientRect(hWnd, &rc);
  524.  
  525.     ghWndTime = CreateWindow(
  526.         gszTWndClass,
  527.         NULL,
  528.         WS_CHILD,
  529.         rc.left, rc.top,
  530.         rc.right-rc.left, rc.bottom-rc.top,
  531.         hWnd,
  532.         NULL,
  533.         lpCreateStruct->hInstance,
  534.         NULL);
  535.  
  536.     ShowWindow(ghWndTime, SW_RESTORE);
  537.  
  538.     wsprintf(gszAppTitle, gszAppTitleMask, (LPSTR)gszUntitled);
  539.     SetWindowText(hWnd, gszAppTitle);
  540.  
  541.     DragAcceptFiles(hWnd, TRUE);
  542.  
  543.     SyncUI(hWnd);
  544.  
  545.     return TRUE;
  546. }
  547.                       
  548. /*****************************************************************************
  549. *
  550. * MWnd_OnGetMinMaxSize
  551. *
  552. * Handle WM_GETMINMAXSIZE message to main application window.
  553. *
  554. * HWND hWnd                 - Window handle
  555. * MINMAXINFO FAR* lpMinMaxInfo
  556. *                           - Pointer to min/max tracking information
  557. *
  558. * This message is sent to a window before resize tracking begins. The
  559. * lpMinMaxInfo structure contains the minimum and maximum x and y values
  560. * the window can be resized to.
  561. *
  562. * We don't allow resizing small enough to cause the status bar and toolbar
  563. * to overlap so they don't try to draw over each other. 
  564. *
  565. *****************************************************************************/
  566. PRIVATE VOID FNLOCAL MWnd_OnGetMinMaxInfo(
  567.     HWND                    hWnd,
  568.     MINMAXINFO FAR*         lpMinMaxInfo)
  569. {
  570.     RECT                    rc;
  571.  
  572.     GetWindowRect(hWnd, &rc);
  573.  
  574.     /* Only go small enough that our client area after children is zero.
  575.     */
  576.     lpMinMaxInfo->ptMinTrackSize.y =
  577.         (rc.bottom - rc.top) -
  578.         (grcTWnd.bottom - grcTWnd.top);                                      
  579. }
  580.  
  581. /*****************************************************************************
  582. *
  583. * MWnd_OnSize
  584. *
  585. * Handle WM_SIZE message to main application window.
  586. *
  587. * HWND hWnd                 - Window handle
  588. * UINT state                - Some SIZE_xxx code indicating what type of
  589. *                             size operation this is.
  590. * int  cx, cy               - New x and y size of the window's client area.
  591. *
  592. * Get the new client area.
  593. * Adjust the client area for the toolbar and status bar if they exist.
  594. * Make sure the client area isn't a negative height and adjust if it is.
  595. * Resize the time window to fit in our new client area.
  596. * Forward the WM_SIZE to the time window so it can resize its font.
  597. *
  598. *****************************************************************************/
  599. PRIVATE VOID FNLOCAL MWnd_OnSize(
  600.     HWND                    hWnd,
  601.     UINT                    state,
  602.     int                     cx,
  603.     int                     cy)
  604. {
  605.     RECT                    rc;
  606.     RECT                    rcClient;
  607.  
  608.     GetClientRect(hWnd, &rcClient);
  609.     if (ghWndToolbar != NULL)
  610.     {
  611.         /* Cause the toolbar to be aware of the size change
  612.         */
  613.         FORWARD_WM_SIZE(ghWndToolbar, SIZE_RESTORED, 0, 0, SendMessage);
  614.         
  615.         GetWindowRect(ghWndToolbar, &rc);
  616.         rcClient.top += (rc.bottom - rc.top);
  617.     }
  618.  
  619.     if (ghWndStatus != NULL)
  620.     {
  621.         ResizeStatusBar(hWnd);
  622.         
  623.         /* Cause the status bar to be aware of the size change
  624.         */
  625.         FORWARD_WM_SIZE(ghWndStatus, SIZE_RESTORED, 0, 0, SendMessage);
  626.         
  627.         GetWindowRect(ghWndStatus, &rc);
  628.         rcClient.bottom -= (rc.bottom - rc.top);
  629.     }
  630.  
  631.     /* Do we need to resize entire window so the tool/status bars
  632.     ** don't overlap? (The only case where this can happen is
  633.     ** on creation of one of the two -- we set minimum tracking so
  634.     ** a user can't manually resize the window to cause this
  635.     ** condition).
  636.     */
  637.     if (rcClient.bottom < rcClient.top)
  638.     {
  639.         GetWindowRect(hWnd, &rc);
  640.         SetWindowPos(hWnd,
  641.                      (HWND)NULL,
  642.                      0, 0,
  643.                      rc.right - rc.left + 1,
  644.                      rc.bottom - rc.top + 1 - rcClient.top - rcClient.bottom,
  645.                      SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
  646.     }
  647.  
  648.     SetWindowPos(ghWndTime,
  649.                  (HWND)NULL,
  650.                  rcClient.left,
  651.                  rcClient.top,
  652.                  rcClient.right - rcClient.left,
  653.                  rcClient.bottom - rcClient.top,
  654.                  SWP_NOACTIVATE|SWP_NOZORDER);
  655.  
  656.     FORWARD_WM_SIZE(ghWndTime, SIZE_RESTORED, 0, 0, SendMessage);
  657. }
  658.  
  659. /*****************************************************************************
  660. *
  661. * MWnd_OnPaint
  662. *
  663. * Handle WM_PAINT message to main application window.
  664. *
  665. * HWND hWnd                 - Window handle
  666. *
  667. * Just do a BeginPaint/EndPaint pair so USER will mark the area
  668. *   as valid. All the real work of painting the time is done
  669. *   by the WM_PAINT handler for the time window.
  670. *
  671. *****************************************************************************/
  672. PRIVATE VOID FNLOCAL MWnd_OnPaint(
  673.     HWND                    hWnd)
  674. {
  675.     PAINTSTRUCT             ps;
  676.     HDC                     hDC;
  677.  
  678.     hDC = BeginPaint(hWnd, &ps);
  679.     EndPaint(hWnd, &ps);
  680. }
  681.  
  682. /*****************************************************************************
  683. *
  684. * MWnd_OnDropFiles
  685. *
  686. * Handle WM_DROPFILES message to main application window.
  687. *
  688. * HWND hWnd                 - Window handle
  689. * HDROP hDrop               - Handle to dropped file information
  690. *
  691. * Get the 0th filename and free the drop handle.
  692. * Extract the file title from the full pathname.
  693. * Open the file.
  694. * If we opened successfully, start playback by forwarding a WM_COMMAND
  695. *   of IDM_PLAY to the main window.
  696. *
  697. *****************************************************************************/
  698. PRIVATE VOID FNLOCAL MWnd_OnDropFiles(
  699.     HWND                    hWnd,
  700.     HDROP                   hDrop)
  701. {
  702.     PSTR                    pStr;
  703.     
  704.     /* For multiple selections, we only accept the first file
  705.     */
  706.     DragQueryFile(hDrop, 0, gszOpenName, sizeof(gszOpenName));
  707.     DragFinish(hDrop);
  708.  
  709.     /* We don't get OpenTitle like we do from GetOpenFileName; need to
  710.     ** figure this out for ourselves
  711.     */
  712.     pStr = gszOpenName + lstrlen(gszOpenName) - 1;
  713.  
  714.     while (pStr >= gszOpenName && *pStr != '/' && *pStr != '\\' && *pStr != ':')
  715.         pStr--;
  716.  
  717.     pStr++;
  718.     lstrcpy(gszOpenTitle, pStr);
  719.  
  720.     AttemptFileOpen(hWnd);
  721.  
  722.     if (gbAutoPlay && gpSeq->uState == SEQ_S_PREROLLED)
  723.         FORWARD_WM_COMMAND(hWnd, IDM_PLAY, (HWND)NULL, 0, SendMessage);
  724. }
  725.  
  726.  
  727. /*****************************************************************************
  728. *
  729. * MWnd_OnFileOpen
  730. *
  731. * Handle WM_COMMAND/IDM_OPEN message to main application window.
  732. *
  733. * HWND hWnd                 - Window handle
  734. *
  735. * Fill in the OPENFILENAME struct and call GetOpenFileName.
  736. * If not canceled, try to open the file.
  737. *
  738. *****************************************************************************/
  739. PRIVATE VOID FNLOCAL MWnd_OnFileOpen(
  740.     HWND                    hWnd)
  741. {
  742.     OPENFILENAME            ofn;
  743.  
  744.     *gszOpenName = '\0';
  745.     
  746.     ofn.lStructSize            = sizeof(OPENFILENAME);
  747.     ofn.hwndOwner            = hWnd;
  748.     ofn.lpstrFilter            = gszFilter;
  749.     ofn.lpstrCustomFilter    = (LPSTR)NULL;
  750.     ofn.nMaxCustFilter        = 0L;
  751.     ofn.nFilterIndex        = 1L;
  752.     ofn.lpstrFile            = gszOpenName;
  753.     ofn.nMaxFile            = MAX_FILEPATH;
  754.     ofn.lpstrFileTitle        = gszOpenTitle;
  755.     ofn.nMaxFileTitle        = MAX_FILEPATH;
  756.     ofn.lpstrTitle            = (LPSTR)NULL;
  757.     ofn.lpstrInitialDir        = (LPSTR)NULL;
  758.     ofn.Flags                = OFN_HIDEREADONLY|OFN_FILEMUSTEXIST;
  759.     ofn.nFileOffset            = 0;
  760.     ofn.nFileExtension        = 0;
  761.     ofn.lpstrDefExt            = gszDefExtension;
  762.  
  763.     if (!GetOpenFileName(&ofn))
  764.         return;
  765.  
  766.     AttemptFileOpen(hWnd);
  767. }
  768.  
  769. /*****************************************************************************
  770. *
  771. * MWnd_OnCommandToggleChild
  772. *
  773. * Handle WM_COMMAND message of toggle tool or status bar to main application
  774. * window.
  775. *
  776. * HWND hWnd                 - Window handle
  777. * UINT id                   - Control id of menu selection; either
  778. *                             IDM_TOOLBAR or IDM_STATUS
  779. *
  780. * Get the current menu item check state.
  781. * Destroy or create the child as needed.
  782. * Send a WM_SIZE to the main window so client area will be recalculated.
  783. * Toggle the menu item check state.
  784. *
  785. *****************************************************************************/
  786. PRIVATE VOID FNLOCAL MWnd_OnCommandToggleChild(
  787.     HWND                    hWnd,                                      
  788.     UINT                    id)
  789. {
  790.     HMENU                   hMenu;
  791.     UINT                    uState;
  792.     HWND*                   phWnd;
  793.     
  794.     phWnd = (id == IDM_TOOLBAR) ? &ghWndToolbar : &ghWndStatus;
  795.  
  796.     hMenu = GetMenu(hWnd);
  797.     uState = GetMenuState(hMenu, id, MF_BYCOMMAND);
  798.     if (uState & MF_CHECKED)
  799.     {
  800.         DestroyWindow(*phWnd);
  801.         *phWnd = NULL;
  802.     }
  803.     else
  804.     {
  805.         if (id == IDM_TOOLBAR)
  806.             InitToolbar(hWnd);
  807.         else
  808.             InitStatusBar(hWnd);
  809.     }
  810.  
  811.     SendMessage(hWnd, WM_SIZE, 0, 0L);
  812.  
  813.     uState ^= MF_CHECKED;
  814.     uState &= MF_CHECKED;
  815.     CheckMenuItem(hMenu, id, MF_BYCOMMAND|uState);
  816.  
  817.     SyncUI(hWnd);
  818. }
  819.                                           
  820.                             
  821. /*****************************************************************************
  822. *
  823. * MWnd_OnCommand
  824. *
  825. * Handle WM_COMMAND message to main application window.
  826. *
  827. * HWND hWnd                 - Window handle
  828. * int id                    - id of control or menu causing WM_COMMAND
  829. * HWND hwndCtl              - Window handle of child control, if any
  830. * UINT codeNotify           - Notification code if this message is from a
  831. *                             control.
  832. *
  833. * For a press of the toolbar buttons or their menu equivalents, just load
  834. * a resource string and display it in the status bar.
  835. *
  836. * For an exit request, send ourselves a WM_CLOSE message.
  837. *
  838. *****************************************************************************/
  839. PRIVATE VOID FNLOCAL MWnd_OnCommand(
  840.     HWND                    hWnd,
  841.     int                     id,
  842.     HWND                    hWndCtl,
  843.     UINT                    codeNotify)
  844. {
  845.     HMENU                   hMenu;
  846.     int                     nIdxFormat;
  847.     LPSTR                   lpstr;
  848.     
  849.     if (id >= IDS_TF_FIRST && id <= IDS_TF_LAST)
  850.     {
  851.         if (NULL != ghWndStatus)
  852.         {
  853.             nIdxFormat = id - IDS_TF_FIRST;
  854.  
  855.             lpstr = (LPSTR)(grgszTimeFormats[nIdxFormat]);
  856.             
  857.             SendMessage(ghWndStatus,
  858.                         SB_SETTEXT,
  859.                         SB_PANE_TFMT,
  860.                         (LPARAM)lpstr);
  861.  
  862.         }
  863.  
  864.         hMenu = GetMenu(hWnd);
  865.  
  866.         CheckMenuItem(hMenu, gnTimeFormat, MF_UNCHECKED|MF_BYCOMMAND);
  867.         CheckMenuItem(hMenu, id, MF_CHECKED|MF_BYCOMMAND);
  868.  
  869.         gnTimeFormat = id;
  870.         
  871.         /* Force time window to update font and repaint entire time string
  872.          */
  873.         
  874.         if(ghWndTime)    // for when WM_COMMAND is called before WM_CREATE
  875.             FORWARD_WM_SIZE(ghWndTime, SIZE_RESTORED, 0, 0, SendMessage);
  876.     }
  877.     else if (id >= IDM_DEVMIN && id <= IDM_DEVMAX)
  878.     {
  879.         hMenu = GetMenu(hWnd);
  880.                 
  881.         CheckMenuItem(hMenu, guDevice + IDM_DEVICES, MF_UNCHECKED|MF_BYCOMMAND);
  882.         guDevice = id - IDM_DEVICES;
  883.         CheckMenuItem(hMenu, guDevice + IDM_DEVICES, MF_CHECKED|MF_BYCOMMAND);
  884.         
  885.         if (gpSeq->uState == SEQ_S_PREROLLED)
  886.             PrerollAndWait(hWnd);
  887.     }
  888.     else switch(id)
  889.     {
  890.         case IDM_OPEN:
  891.             MWnd_OnFileOpen(hWnd);
  892.             break;
  893.         
  894.         case IDM_TOOLBAR:
  895.         case IDM_STATUS:
  896.             MWnd_OnCommandToggleChild(hWnd, id);
  897.             break;
  898.  
  899.         case IDM_AUTOPLAY:
  900.             gbAutoPlay = !gbAutoPlay;
  901.             CheckMenuItem(GetMenu(hWnd),
  902.                           IDM_AUTOPLAY,
  903.                           MF_BYCOMMAND|(gbAutoPlay ? MF_CHECKED : MF_UNCHECKED));
  904.             break;
  905.  
  906.         case IDM_PLAY:
  907.             FORWARD_WM_COMMAND(ghWndTime, IDM_PLAY, 0, 0, SendMessage);
  908.                                
  909.             seqStart(gpSeq);
  910.             SyncUI(hWnd);
  911.             break;
  912.  
  913.         case IDM_STOP:
  914.             FORWARD_WM_COMMAND(ghWndTime, IDM_STOP, 0, 0, SendMessage);
  915.             seqStop(gpSeq);
  916.             SyncUI(hWnd);
  917.             break;
  918.  
  919.         case IDM_PAUSE:
  920.             if (gpSeq->uState == SEQ_S_PAUSED)
  921.             {
  922.                 seqRestart(gpSeq);
  923.             }
  924.             else
  925.             {
  926.                 seqPause(gpSeq);
  927.             }
  928.  
  929.             SyncUI(hWnd);
  930.             break;
  931.  
  932.         case IDM_SYNCUI:
  933.             SyncUI(hWnd);
  934.             break;
  935.             
  936.         case IDM_EXIT:
  937.             SendMessage(hWnd, WM_CLOSE, 0, 0L);
  938.             break;
  939.     }
  940. }
  941.                                    
  942. /*****************************************************************************
  943. *
  944. * MWnd_OnDestroy
  945. *
  946. * Handle WM_DESTROY message to main application window.
  947. *
  948. * HWND hWnd                 - Window handle
  949. *
  950. * Our main application window has been closed. PostQuitMessage so the main
  951. * message loop will exit and the app can terminate.
  952. *
  953. *****************************************************************************/
  954. PRIVATE VOID FNLOCAL MWnd_OnDestroy(
  955.     HWND                    hWnd)
  956. {
  957.     seqStop(gpSeq);
  958.     PostQuitMessage(0);
  959. }
  960.  
  961. /*****************************************************************************
  962. *
  963. * MWnd_WndProc
  964. *
  965. * Window procedure for main application window.
  966. *
  967. * HWND hWnd                 - Window handle
  968. * UINT msg                  - Message code
  969. * WPARAM wParam             - Message specific parameter
  970. * LPARAM lParam             - Message specific parameter
  971. *
  972. * Dispatch messages we care about to the appropriate handler, else just
  973. * call DefWindowProc.
  974. *
  975. * Note this use of message cracker macros from windowsx.h. Using these
  976. * macros will shield you from the differences between Win16 and Win32;
  977. * if your app is cross-compilable, you should use these and save yourself
  978. * some headaches!
  979. *
  980. *****************************************************************************/
  981. LRESULT CALLBACK MWnd_WndProc(
  982.     HWND                    hWnd,
  983.     UINT                    msg,
  984.     WPARAM                  wParam,
  985.     LPARAM                  lParam)
  986. {
  987.     switch( msg )
  988.     {
  989.         HANDLE_MSG(hWnd, WM_CREATE,         MWnd_OnCreate);
  990.         HANDLE_MSG(hWnd, WM_GETMINMAXINFO,  MWnd_OnGetMinMaxInfo);
  991.         HANDLE_MSG(hWnd, WM_SIZE,           MWnd_OnSize);
  992.         HANDLE_MSG(hWnd, WM_PAINT,          MWnd_OnPaint);
  993.         HANDLE_MSG(hWnd, WM_DROPFILES,      MWnd_OnDropFiles);
  994.         HANDLE_MSG(hWnd, WM_COMMAND,        MWnd_OnCommand);
  995.         HANDLE_MSG(hWnd, WM_DESTROY,        MWnd_OnDestroy);
  996.  
  997.         case MM_MOM_DONE:
  998.             seqBufferDone((LPMIDIHDR)lParam);
  999.             break;
  1000.  
  1001.         case MMSG_DONE:
  1002.             FORWARD_WM_COMMAND(ghWndTime, IDM_STOP, 0, 0, SendMessage);
  1003.             
  1004.             if (gpSeq->uState == SEQ_S_OPENED)
  1005.                 PrerollAndWait(hWnd);
  1006.             
  1007.             SyncUI(hWnd);
  1008.             break;
  1009.  
  1010.         default:
  1011.             return DefWindowProc(hWnd, msg, wParam, lParam);
  1012.     }
  1013.  
  1014.     return 0;
  1015. }
  1016.