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