Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3874 lines
88 KiB

  1. /******************************Module*Header*******************************\
  2. * Module Name: cdplayer.c
  3. *
  4. * CD Playing application
  5. *
  6. *
  7. * Created: 02-11-93
  8. * Author: Stephen Estrop [StephenE]
  9. *
  10. * Copyright (c) 1993 - 1995 Microsoft Corporation. All rights reserved.
  11. \**************************************************************************/
  12. #pragma warning( once : 4201 4214 )
  13. #include <windows.h> /* required for all Windows applications */
  14. #include <shellapi.h>
  15. #include <windowsx.h>
  16. #include <ole2.h>
  17. #include <shlobj.h>
  18. #include <dbt.h>
  19. #include "..\main\mmfw.h"
  20. #include "..\main\resource.h"
  21. #include "..\main\mmenu.h"
  22. #include "..\cdopt\cdopt.h"
  23. #include "..\cdnet\cdnet.h"
  24. #define NOMENUHELP
  25. #define NOBTNLIST
  26. #define NOTRACKBAR
  27. #define NODRAGLIST
  28. #define NOUPDOWN
  29. #include <commctrl.h> /* want toolbar and status bar */
  30. #include <string.h>
  31. #include <stdio.h>
  32. #include <tchar.h> /* contains portable ascii/unicode macros */
  33. #include <stdarg.h>
  34. #include <stdlib.h>
  35. #define GLOBAL /* This allocates storage for the public globals */
  36. #include "playres.h"
  37. #include "cdplayer.h"
  38. #include "ledwnd.h"
  39. #include "cdapi.h"
  40. #include "scan.h"
  41. #include "trklst.h"
  42. #include "database.h"
  43. #include "commands.h"
  44. #include "literals.h"
  45. //#ifndef WM_CDPLAYER_COPYDATA
  46. #define WM_CDPLAYER_COPYDATA (WM_USER+0x100)
  47. //#endif
  48. IMMFWNotifySink* g_pSink;
  49. ATOM g_atomCDClass = NULL;
  50. extern HINSTANCE g_hInst;
  51. void GetTOC(int cdrom, TCHAR* szNetQuery);
  52. /* -------------------------------------------------------------------------
  53. ** Private functions
  54. ** -------------------------------------------------------------------------
  55. */
  56. int
  57. CopyWord(
  58. TCHAR *szWord,
  59. TCHAR *szSource
  60. );
  61. void
  62. AppendTrackToPlayList(
  63. PTRACK_PLAY pHead,
  64. PTRACK_PLAY pInsert
  65. );
  66. BOOL
  67. IsTrackFileNameValid(
  68. LPTSTR lpstFileName,
  69. int *piCdRomIndex,
  70. int *piTrackIndex,
  71. BOOL fScanningTracks,
  72. BOOL fQuiet
  73. );
  74. TCHAR *
  75. ParseTrackList(
  76. TCHAR *szTrackList,
  77. int *piCdRomIndex
  78. );
  79. int
  80. ParseCommandLine(
  81. LPTSTR lpstr,
  82. int *piTrackToSeekTo,
  83. BOOL fQuiet
  84. );
  85. void
  86. HandlePassedCommandLine(
  87. LPTSTR lpCmdLine,
  88. BOOL fCheckCDRom
  89. );
  90. int
  91. FindMostSuitableDrive(
  92. void
  93. );
  94. void
  95. AskUserToInsertCorrectDisc(
  96. DWORD dwID
  97. );
  98. #ifndef USE_IOCTLS
  99. BOOL CheckMCICDA (TCHAR chDrive);
  100. #endif // ! USE_IOCTLS
  101. BOOL
  102. CDPlay_CopyData(
  103. HWND hwnd,
  104. PCOPYDATASTRUCT lpcpds
  105. );
  106. /* -------------------------------------------------------------------------
  107. ** Private Globals
  108. ** -------------------------------------------------------------------------
  109. */
  110. HBRUSH g_hBrushBkgd;
  111. TCHAR g_szTimeSep[10];
  112. int g_AcceleratorCount;
  113. BOOL g_fInCopyData = FALSE;
  114. HCURSOR g_hcurs = NULL;
  115. //---------------------------------------------------------------------------
  116. // Stuff required to make drag/dropping of a shortcut file work on Chicago
  117. //---------------------------------------------------------------------------
  118. BOOL
  119. ResolveLink(
  120. TCHAR * szFileName
  121. );
  122. BOOL g_fOleInitialized = FALSE;
  123. /*
  124. ** these values are defined by the UI gods...
  125. */
  126. const int dxButton = 24;
  127. const int dyButton = 22;
  128. const int dxBitmap = 16;
  129. const int dyBitmap = 16;
  130. const int xFirstButton = 8;
  131. /******************************Public*Routine******************************\
  132. * WinMain
  133. *
  134. *
  135. * Windows recognizes this function by name as the initial entry point
  136. * for the program. This function calls the application initialization
  137. * routine, if no other instance of the program is running, and always
  138. * calls the instance initialization routine. It then executes a message
  139. * retrieval and dispatch loop that is the top-level control structure
  140. * for the remainder of execution. The loop is terminated when a WM_QUIT
  141. * message is received, at which time this function exits the application
  142. * instance by returning the value passed by PostQuitMessage().
  143. *
  144. * If this function must abort before entering the message loop, it
  145. * returns the conventional value NULL.
  146. *
  147. *
  148. * History:
  149. * 18-11-93 - StephenE - Created
  150. *
  151. \**************************************************************************/
  152. HWND PASCAL
  153. WinFake(
  154. HINSTANCE hInstance,
  155. HINSTANCE hPrevInstance,
  156. LPSTR lpCmdLine,
  157. int nCmdShow,
  158. HWND hwndMain,
  159. IMMFWNotifySink* pSink
  160. )
  161. {
  162. g_pSink = pSink;
  163. g_fBlockNetPrompt = FALSE;
  164. g_fSelectedOrder = TRUE;
  165. g_fIntroPlay = FALSE;
  166. g_fContinuous = FALSE;
  167. g_fRepeatSingle = FALSE;
  168. #ifdef DBG
  169. /*
  170. ** This removes the Gdi batch feature. It ensures that the screen
  171. ** is updated after every gdi call - very useful for debugging.
  172. */
  173. GdiSetBatchLimit(1);
  174. #endif
  175. /*
  176. ** Save the instance handle in static variable, which will be used in
  177. ** many subsequence calls from this application to Windows.
  178. */
  179. g_hInst = hInstance;
  180. g_lpCmdLine = lpCmdLine;
  181. InitializeCriticalSection (&g_csTOCSerialize);
  182. /*
  183. ** Initialize the cdplayer application.
  184. */
  185. CdPlayerStartUp(hwndMain);
  186. return g_hwndApp;
  187. }
  188. /*****************************Private*Routine******************************\
  189. * InitInstance
  190. *
  191. *
  192. * This function is called at initialization time for every instance of
  193. * this application. This function performs initialization tasks that
  194. * cannot be shared by multiple instances.
  195. *
  196. * In this case, we save the instance handle in a static variable and
  197. * create and display the main program window.
  198. *
  199. * History:
  200. * 18-11-93 - StephenE - Created
  201. *
  202. \**************************************************************************/
  203. BOOL
  204. InitInstance(
  205. HANDLE hInstance,
  206. HWND hwndMain
  207. )
  208. {
  209. HWND hwnd;
  210. /*
  211. ** Load in some strings
  212. */
  213. _tcscpy( g_szArtistTxt, IdStr( STR_HDR_ARTIST ) );
  214. _tcscpy( g_szTitleTxt, IdStr( STR_HDR_TITLE ) );
  215. _tcscpy( g_szUnknownTxt, IdStr( STR_UNKNOWN ) );
  216. _tcscpy( g_szTrackTxt, IdStr( STR_HDR_TRACK ) );
  217. g_szTimeSep[0] = TEXT(':');
  218. g_szTimeSep[1] = g_chNULL;
  219. GetLocaleInfo( GetUserDefaultLCID(), LOCALE_STIME, g_szTimeSep, 10 );
  220. /*
  221. ** Initialize the my classes. We do this here because the dialog
  222. ** that we are about to create contains two windows on my class.
  223. ** The dialog would fail to be created if the classes was not registered.
  224. */
  225. g_fDisplayT = TRUE;
  226. InitLEDClass( g_hInst );
  227. Init_SJE_TextClass( g_hInst );
  228. WNDCLASS cls;
  229. cls.lpszClassName = g_szSJE_CdPlayerClass;
  230. cls.hCursor = NULL; //LoadCursor(NULL, IDC_ARROW);
  231. cls.hIcon = NULL;
  232. cls.lpszMenuName = NULL;
  233. cls.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
  234. cls.hInstance = (HINSTANCE)hInstance;
  235. cls.style = CS_DBLCLKS;
  236. cls.lpfnWndProc = DefDlgProc;
  237. cls.cbClsExtra = 0;
  238. cls.cbWndExtra = DLGWINDOWEXTRA;
  239. if ( !RegisterClass(&cls) )
  240. {
  241. return FALSE;
  242. }
  243. g_hcurs = LoadCursor(g_hInst,MAKEINTRESOURCE(IDC_CURSOR_HAND));
  244. /*
  245. ** Create a main window for this application instance.
  246. */
  247. hwnd = CreateDialog( g_hInst, MAKEINTRESOURCE(IDR_CDPLAYER),
  248. hwndMain, MyMainWndProc );
  249. /*
  250. ** If window could not be created, return "failure"
  251. */
  252. if ( !hwnd )
  253. {
  254. return FALSE;
  255. }
  256. g_hwndApp = hwnd;
  257. return TRUE;
  258. }
  259. /*****************************Private*Routine******************************\
  260. * CdPlayerStartUp
  261. *
  262. *
  263. *
  264. * History:
  265. * dd-mm-95 - StephenE - Created
  266. *
  267. \**************************************************************************/
  268. void
  269. CdPlayerStartUp(
  270. HWND hwndMain
  271. )
  272. {
  273. /*
  274. ** Reseed random generator
  275. */
  276. srand( GetTickCount() );
  277. /*
  278. ** Set error mode popups for critical errors (like
  279. ** no disc in drive) OFF.
  280. */
  281. SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX );
  282. /*
  283. ** Scan device chain for CDROM devices... Terminate if none found.
  284. */
  285. g_NumCdDevices = ScanForCdromDevices( );
  286. if ( g_NumCdDevices == 0 )
  287. {
  288. LPTSTR lpstrTitle;
  289. LPTSTR lpstrText;
  290. lpstrTitle = (TCHAR*)AllocMemory( STR_MAX_STRING_LEN * sizeof(TCHAR) );
  291. lpstrText = (TCHAR*)AllocMemory( STR_MAX_STRING_LEN * sizeof(TCHAR) );
  292. _tcscpy( lpstrText, IdStr(STR_NO_CDROMS) );
  293. _tcscpy( lpstrTitle, IdStr(STR_CDPLAYER) );
  294. MessageBox( NULL, lpstrText, lpstrTitle,
  295. MB_APPLMODAL | MB_ICONINFORMATION |
  296. MB_OK | MB_SETFOREGROUND );
  297. LocalFree( (HLOCAL)lpstrText );
  298. LocalFree( (HLOCAL)lpstrTitle );
  299. ExitProcess( (UINT)-1 );
  300. }
  301. #ifndef USE_IOCTLS
  302. // Make sure we have a functional MCI (CD Audio)
  303. OSVERSIONINFO os;
  304. os.dwOSVersionInfoSize = sizeof(os);
  305. GetVersionEx(&os);
  306. if (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
  307. {
  308. if (! CheckMCICDA (g_Devices[0]->drive))
  309. {
  310. ExitProcess( (UINT)-1 );
  311. }
  312. }
  313. #endif // ! USE_IOCTLS
  314. /*
  315. ** Perform initializations that apply to a specific instance
  316. ** This function actually creates the CdPlayer window. (Note that it is
  317. ** not visible yet). If we get here we know that there is a least one
  318. ** cdrom device detected which may have a music cd in it. If it does
  319. ** contain a music cdrom the table of contents will have been read and
  320. ** cd database queryed to determine if the music cd is known. Therefore
  321. ** on the WM_INITDIALOG message we should update the "Artist", "Title" and
  322. ** "Track" fields of the track info display and adjust the enable state
  323. ** of the play buttons.
  324. */
  325. if ( !InitInstance( g_hInst, hwndMain ) )
  326. {
  327. FatalApplicationError( STR_TERMINATE );
  328. }
  329. /*
  330. ** Restore ourselves from the ini file
  331. */
  332. ReadSettings(NULL);
  333. /*
  334. ** Scan command the command line. If we were given any valid commandline
  335. ** args we have to adjust the nCmdShow parameter. (ie. start minimized
  336. ** if the user just wants us to play a certain track. ScanCommandLine can
  337. ** overide the default playlist for all the cd-rom devices installed. It
  338. ** modifies the global flag g_fPlay and returns the index of the first
  339. ** CD-Rom that should be played.
  340. */
  341. g_CurrCdrom = g_LastCdrom = 0;
  342. }
  343. /*****************************Private*Routine******************************\
  344. * CompleteCdPlayerStartUp
  345. *
  346. *
  347. *
  348. * History:
  349. * dd-mm-95 - StephenE - Created
  350. *
  351. \**************************************************************************/
  352. void
  353. CompleteCdPlayerStartUp(
  354. void
  355. )
  356. {
  357. int iTrackToSeekTo = -1;
  358. int i;
  359. g_fStartedInTray = FALSE;
  360. /*
  361. ** Scan command the command line. If we were given any valid
  362. ** commandline args we have to adjust the nCmdShow parameter. (ie.
  363. ** start minimized if the user just wants us to play a certain
  364. ** track. ScanCommandLine can overide the default playlist for all
  365. ** the cd-rom devices installed. It modifies the global flag
  366. ** g_fPlay and returns the index of the first CD-Rom that should be
  367. ** played.
  368. **
  369. */
  370. g_CurrCdrom = g_LastCdrom = ParseCommandLine( GetCommandLine(),
  371. &iTrackToSeekTo, FALSE );
  372. /*
  373. ** If the message box prompting the user to insert the correct cd disc in
  374. ** the drive was displayed, ParseCommandLine will return -1, in which case
  375. ** find the most suitable drive, also make sure that we don't come up
  376. ** playing.
  377. */
  378. if (g_LastCdrom == -1)
  379. {
  380. g_fPlay = FALSE;
  381. g_CurrCdrom = g_LastCdrom = FindMostSuitableDrive();
  382. }
  383. for ( i = 0; i < g_NumCdDevices; i++) {
  384. TimeAdjustInitialize( i );
  385. }
  386. /*
  387. ** All the rescan threads are either dead or in the act of dying.
  388. ** It is now safe to initalize the time information for each
  389. ** cdrom drive.
  390. */
  391. if ( iTrackToSeekTo != -1 ) {
  392. PTRACK_PLAY tr;
  393. tr = PLAYLIST( g_CurrCdrom );
  394. if ( tr != NULL ) {
  395. for( i = 0; i < iTrackToSeekTo; i++, tr = tr->nextplay );
  396. TimeAdjustSkipToTrack( g_CurrCdrom, tr );
  397. }
  398. }
  399. /*
  400. ** if we are in random mode, then we need to shuffle the play lists.
  401. */
  402. if (!g_fSelectedOrder)
  403. {
  404. ComputeAndUseShufflePlayLists();
  405. }
  406. SetPlayButtonsEnableState();
  407. /*
  408. ** Start the heart beat time. This timer is responsible for:
  409. ** 1. detecting new or ejected cdroms.
  410. ** 2. flashing the LED display if we are in paused mode.
  411. ** 3. Incrementing the LED display if we are in play mode.
  412. */
  413. UINT_PTR timerid = SetTimer( g_hwndApp, HEARTBEAT_TIMER_ID, HEARTBEAT_TIMER_RATE,
  414. (TIMERPROC)HeartBeatTimerProc );
  415. if (!g_fPlay)
  416. {
  417. //"play" wasn't on the command line, but maybe the user wants it on startup anyway
  418. HKEY hKey;
  419. LONG lRet;
  420. lRet = RegOpenKey( HKEY_CURRENT_USER, g_szRegistryKey, &hKey );
  421. if ( (lRet == ERROR_SUCCESS) )
  422. {
  423. DWORD dwType, dwLen;
  424. dwLen = sizeof( g_fPlay );
  425. if ( ERROR_SUCCESS != RegQueryValueEx(hKey, g_szStartCDPlayingOnStart,
  426. 0L, &dwType, (LPBYTE)&g_fPlay,
  427. &dwLen) )
  428. {
  429. g_fPlay = FALSE; //default to not playing
  430. }
  431. RegCloseKey(hKey);
  432. }
  433. }
  434. //Don't start if player was started in tray mode.
  435. //This prevents the user from getting an unexpected blast on boot.
  436. if (( g_fPlay ) && (!g_fStartedInTray))
  437. {
  438. CdPlayerPlayCmd();
  439. }
  440. if (g_CurrCdrom != 0)
  441. {
  442. //didn't use the default player, so jump to the new one
  443. MMONDISCCHANGED mmOnDisc;
  444. mmOnDisc.nNewDisc = g_CurrCdrom;
  445. mmOnDisc.fDisplayVolChange = FALSE;
  446. g_pSink->OnEvent(MMEVENT_ONDISCCHANGED,&mmOnDisc);
  447. }
  448. if (g_Devices[g_CurrCdrom]->State & CD_LOADED)
  449. {
  450. //need to set track button on main ui
  451. HWND hwndTrackButton = GetDlgItem(GetParent(g_hwndApp),IDB_TRACK);
  452. if (hwndTrackButton)
  453. {
  454. EnableWindow(hwndTrackButton,TRUE);
  455. }
  456. }
  457. //cd was already playing; let's update the main ui
  458. if (g_Devices[g_CurrCdrom]->State & CD_PLAYING)
  459. {
  460. g_pSink->OnEvent(MMEVENT_ONPLAY,NULL);
  461. }
  462. }
  463. /******************************Public*Routine******************************\
  464. * MyMainWndProc
  465. *
  466. * Use the message crackers to dispatch the dialog messages to appropirate
  467. * message handlers. The message crackers are portable between 16 and 32
  468. * bit versions of Windows.
  469. *
  470. * History:
  471. * 18-11-93 - StephenE - Created
  472. *
  473. \**************************************************************************/
  474. INT_PTR CALLBACK
  475. MyMainWndProc(
  476. HWND hwnd,
  477. UINT message,
  478. WPARAM wParam,
  479. LPARAM lParam
  480. )
  481. {
  482. switch ( message ) {
  483. HANDLE_MSG( hwnd, WM_INITDIALOG, CDPlay_OnInitDialog );
  484. HANDLE_MSG( hwnd, WM_DRAWITEM, CDPlay_OnDrawItem );
  485. HANDLE_MSG( hwnd, WM_COMMAND, CDPlay_OnCommand );
  486. HANDLE_MSG( hwnd, WM_DESTROY, CDPlay_OnDestroy );
  487. HANDLE_MSG( hwnd, WM_SIZE, CDPlay_OnSize );
  488. HANDLE_MSG( hwnd, WM_ENDSESSION, CDPlay_OnEndSession );
  489. HANDLE_MSG( hwnd, WM_WININICHANGE, CDPlay_OnWinIniChange );
  490. HANDLE_MSG( hwnd, WM_CTLCOLORSTATIC, Common_OnCtlColor );
  491. HANDLE_MSG( hwnd, WM_CTLCOLORDLG, Common_OnCtlColor );
  492. HANDLE_MSG( hwnd, WM_MEASUREITEM, Common_OnMeasureItem );
  493. HANDLE_MSG( hwnd, WM_NOTIFY, CDPlay_OnNotify );
  494. HANDLE_MSG( hwnd, WM_DROPFILES, CDPlay_OnDropFiles );
  495. case WM_DEVICECHANGE:
  496. return CDPlay_OnDeviceChange (hwnd, wParam, lParam);
  497. case WM_SETFOCUS :
  498. {
  499. //move focus to next window in tab order
  500. HWND hwndNext = GetNextDlgTabItem(GetParent(hwnd),hwnd,FALSE);
  501. //if the next window just lost focus, we need to go the other way
  502. if (hwndNext == (HWND)wParam)
  503. {
  504. hwndNext = GetNextDlgTabItem(GetParent(hwnd),hwnd,TRUE);
  505. }
  506. SetFocus(hwndNext);
  507. return 0;
  508. }
  509. break;
  510. case WM_ERASEBKGND:
  511. return 1;
  512. case WM_CLOSE:
  513. return CDPlay_OnClose(hwnd, FALSE);
  514. case WM_COPYDATA:
  515. return CDPlay_CopyData( hwnd, (PCOPYDATASTRUCT)lParam );
  516. case WM_CDPLAYER_COPYDATA:
  517. return CDPlay_OnCopyData( hwnd, (PCOPYDATASTRUCT)lParam );
  518. case WM_NOTIFY_TOC_READ:
  519. return CDPlay_OnTocRead( (int)wParam );
  520. case WM_NOTIFY_FIRST_SCAN:
  521. {
  522. for ( int i = 0; i < g_NumCdDevices; i++ )
  523. {
  524. RescanDevice( hwnd, i );
  525. }
  526. return TRUE;
  527. }
  528. break;
  529. default:
  530. return FALSE;
  531. }
  532. }
  533. /*****************************Private*Routine******************************\
  534. * CDPlay_OnInitDialog
  535. *
  536. *
  537. *
  538. * History:
  539. * 18-11-93 - StephenE - Created
  540. *
  541. \**************************************************************************/
  542. BOOL
  543. CDPlay_OnInitDialog(
  544. HWND hwnd,
  545. HWND hwndFocus,
  546. LPARAM lParam
  547. )
  548. {
  549. int i;
  550. g_hBrushBkgd = CreateSolidBrush( GetSysColor(COLOR_BTNFACE) );
  551. EnumChildWindows( hwnd, ChildEnumProc, (LPARAM)hwnd );
  552. DragAcceptFiles( hwnd, TRUE );
  553. /*
  554. ** Initialize and read the TOC for all the detected CD-ROMS
  555. */
  556. SetPlayButtonsEnableState();
  557. for ( i = 0; i < g_NumCdDevices; i++ )
  558. {
  559. ASSERT(g_Devices[i]->State == CD_BEING_SCANNED);
  560. ASSERT(g_Devices[i]->hCd == 0L);
  561. TimeAdjustInitialize( i );
  562. g_Devices[i]->State = CD_NO_CD;
  563. }
  564. PostMessage(hwnd,WM_NOTIFY_FIRST_SCAN,0,0);
  565. return FALSE;
  566. }
  567. /*****************************Private*Routine******************************\
  568. * CDPlay_OnWinIniChange
  569. *
  570. * Updates the time format separator and the LED display
  571. *
  572. * History:
  573. * 29-09-94 - StephenE - Created
  574. *
  575. \**************************************************************************/
  576. void
  577. CDPlay_OnWinIniChange(
  578. HWND hwnd,
  579. LPCTSTR lpszSectionName
  580. )
  581. {
  582. GetLocaleInfo( GetUserDefaultLCID(), LOCALE_STIME, g_szTimeSep, 10 );
  583. UpdateDisplay( DISPLAY_UPD_LED | DISPLAY_UPD_DISC_TIME | DISPLAY_UPD_TRACK_TIME );
  584. }
  585. /*****************************Private*Routine******************************\
  586. * CDPlay_OnDrawItem
  587. *
  588. *
  589. *
  590. * History:
  591. * 18-11-93 - StephenE - Created
  592. *
  593. \**************************************************************************/
  594. BOOL
  595. CDPlay_OnDrawItem(
  596. HWND hwnd,
  597. const DRAWITEMSTRUCT *lpdis
  598. )
  599. {
  600. if (lpdis->CtlType == ODT_MENU)
  601. {
  602. return FALSE;
  603. }
  604. int i;
  605. i = INDEX(lpdis->CtlID);
  606. switch (lpdis->CtlType) {
  607. case ODT_BUTTON:
  608. /*
  609. ** See if the fast foreward or backward buttons has been pressed or
  610. ** released. If so execute the seek command here. Do nothing on
  611. ** the WM_COMMAND message.
  612. */
  613. if ( lpdis->CtlID == IDM_PLAYBAR_SKIPBACK
  614. || lpdis->CtlID == IDM_PLAYBAR_SKIPFORE ) {
  615. if (lpdis->itemAction & ODA_SELECT ) {
  616. g_AcceleratorCount = 0;
  617. CdPlayerSeekCmd( hwnd, (lpdis->itemState & ODS_SELECTED),
  618. lpdis->CtlID );
  619. }
  620. }
  621. /*
  622. ** Now draw the button according to the buttons state information.
  623. */
  624. /*
  625. case ODT_COMBOBOX:
  626. if (lpdis->itemAction & (ODA_DRAWENTIRE | ODA_SELECT)) {
  627. switch (lpdis->CtlID) {
  628. case IDC_ARTIST_NAME:
  629. DrawDriveItem( lpdis->hDC, &lpdis->rcItem,
  630. lpdis->itemData,
  631. (ODS_SELECTED & lpdis->itemState) );
  632. break;
  633. }
  634. }
  635. */
  636. return TRUE;
  637. }
  638. return FALSE;
  639. }
  640. /*****************************Private*Routine******************************\
  641. * CDPlay_OnCommand
  642. *
  643. *
  644. *
  645. * History:
  646. * 18-11-93 - StephenE - Created
  647. *
  648. \**************************************************************************/
  649. void
  650. CDPlay_OnCommand(
  651. HWND hwnd,
  652. int id,
  653. HWND hwndCtl,
  654. UINT codeNotify
  655. )
  656. {
  657. switch( id )
  658. {
  659. case IDM_NET_CD:
  660. {
  661. MMNET* pNet = (MMNET*)hwndCtl;
  662. if (pNet->discid==0)
  663. {
  664. //if disc id is 0, then we want to manually get the info for the current cd
  665. GetInternetDatabase(g_CurrCdrom,g_Devices[g_CurrCdrom]->CdInfo.Id,TRUE,TRUE,pNet->hwndCallback,NULL);
  666. }
  667. else if ((pNet->discid==-1) || (pNet->fForceNet))
  668. {
  669. //if disc id is -1, then we want to get just the batches
  670. int cdrom = g_CurrCdrom;
  671. if (pNet->fForceNet)
  672. {
  673. //try to find the correct cdrom for this guy
  674. for(int i = 0; i < g_NumCdDevices; i++)
  675. {
  676. if (pNet->discid == g_Devices[i]->CdInfo.Id)
  677. {
  678. //if the id was found in the player, physically rescan it
  679. pNet->pData2 = NULL;
  680. cdrom = i;
  681. break;
  682. }
  683. } //end for
  684. } //end if force net
  685. GetInternetDatabase(cdrom,pNet->fForceNet ? pNet->discid : 0,TRUE,TRUE,pNet->hwndCallback,pNet->pData2);
  686. }
  687. else
  688. {
  689. for(int i = 0; i < g_NumCdDevices; i++)
  690. {
  691. if (pNet->discid == g_Devices[i]->CdInfo.Id)
  692. {
  693. //don't hit the net, just scan the entry
  694. GetInternetDatabase(i,g_Devices[i]->CdInfo.Id,FALSE,TRUE,pNet->hwndCallback,pNet->pData);
  695. UpdateDisplay(DISPLAY_UPD_TITLE_NAME|DISPLAY_UPD_LED);
  696. }
  697. } //end for
  698. }
  699. }
  700. break;
  701. case IDM_OPTIONS_NORMAL :
  702. {
  703. //turn randomness off if it is on
  704. if (!g_fSelectedOrder)
  705. {
  706. if ( LockALLTableOfContents() )
  707. {
  708. FlipBetweenShuffleAndOrder();
  709. }
  710. }
  711. g_fRepeatSingle = FALSE;
  712. g_fIntroPlay = FALSE;
  713. g_fSelectedOrder = TRUE;
  714. g_fContinuous = FALSE;
  715. }
  716. break;
  717. case IDM_OPTIONS_RANDOM:
  718. if ( LockALLTableOfContents() )
  719. {
  720. g_fSelectedOrder = FALSE;
  721. ComputeAndUseShufflePlayLists();
  722. g_fIntroPlay = FALSE;
  723. g_fContinuous = TRUE;
  724. g_fRepeatSingle = FALSE;
  725. }
  726. break;
  727. //case IDM_OPTIONS_MULTI:
  728. //g_fSingleDisk = !g_fSingleDisk;
  729. //break;
  730. case IDM_OPTIONS_REPEAT_SINGLE :
  731. {
  732. //turn randomness off if it is on
  733. if (!g_fSelectedOrder)
  734. {
  735. if ( LockALLTableOfContents() )
  736. {
  737. FlipBetweenShuffleAndOrder();
  738. }
  739. }
  740. g_fRepeatSingle = TRUE;
  741. g_fIntroPlay = FALSE;
  742. g_fSelectedOrder = TRUE;
  743. g_fContinuous = FALSE;
  744. }
  745. break;
  746. case IDM_OPTIONS_INTRO:
  747. //turn randomness off if it is on
  748. if (!g_fSelectedOrder)
  749. {
  750. if ( LockALLTableOfContents() )
  751. {
  752. FlipBetweenShuffleAndOrder();
  753. }
  754. }
  755. g_fIntroPlay = TRUE;
  756. g_fSelectedOrder = TRUE;
  757. g_fContinuous = FALSE;
  758. g_fRepeatSingle = FALSE;
  759. break;
  760. case IDM_OPTIONS_CONTINUOUS:
  761. //turn randomness off if it is on
  762. if (!g_fSelectedOrder)
  763. {
  764. if ( LockALLTableOfContents() )
  765. {
  766. FlipBetweenShuffleAndOrder();
  767. }
  768. }
  769. g_fContinuous = TRUE;
  770. g_fIntroPlay = FALSE;
  771. g_fSelectedOrder = TRUE;
  772. g_fRepeatSingle = FALSE;
  773. break;
  774. case IDM_TIME_REMAINING:
  775. g_fDisplayT = TRUE;
  776. g_fDisplayD = g_fDisplayTr = g_fDisplayDr = FALSE;
  777. UpdateDisplay( DISPLAY_UPD_LED );
  778. break;
  779. case IDM_TRACK_REMAINING:
  780. g_fDisplayTr = TRUE;
  781. g_fDisplayD = g_fDisplayDr = g_fDisplayT = FALSE;
  782. UpdateDisplay( DISPLAY_UPD_LED );
  783. break;
  784. case IDM_DISC_REMAINING:
  785. g_fDisplayDr = TRUE;
  786. g_fDisplayD = g_fDisplayTr = g_fDisplayT = FALSE;
  787. UpdateDisplay( DISPLAY_UPD_LED );
  788. break;
  789. case IDM_PLAYBAR_EJECT:
  790. CdPlayerEjectCmd();
  791. break;
  792. case IDM_PLAYBAR_PLAY:
  793. /*
  794. ** If we currently in PLAY mode and the command came from
  795. ** a keyboard accelerator then assume that the user really
  796. ** means Pause. This is because the Ctrl-P key sequence
  797. ** is a toggle between Play and Paused. codeNotify is 1 when
  798. ** the WM_COMMAND message came from an accelerator and 0 when
  799. ** it cam from a menu.
  800. */
  801. if ((g_State & CD_PLAYING) && (codeNotify == 1))
  802. {
  803. CdPlayerPauseCmd();
  804. }
  805. else
  806. {
  807. CdPlayerPlayCmd();
  808. }
  809. break;
  810. case IDM_PLAYBAR_PAUSE:
  811. CdPlayerPauseCmd();
  812. break;
  813. case IDM_PLAYBAR_STOP:
  814. CdPlayerStopCmd();
  815. break;
  816. case IDM_PLAYBAR_PREVTRACK:
  817. CdPlayerPrevTrackCmd();
  818. break;
  819. case IDM_PLAYBAR_NEXTTRACK:
  820. CdPlayerNextTrackCmd();
  821. break;
  822. case IDM_DATABASE_EXIT:
  823. PostMessage( hwnd, WM_CLOSE, 0, 0L );
  824. break;
  825. }
  826. }
  827. /******************************Public*Routine******************************\
  828. * CDPlay_OnDestroy
  829. *
  830. *
  831. *
  832. * History:
  833. * dd-mm-93 - StephenE - Created
  834. *
  835. \**************************************************************************/
  836. void
  837. CDPlay_OnDestroy(
  838. HWND hwnd
  839. )
  840. {
  841. int i;
  842. for ( i = 0; i < g_NumCdDevices; i++ ) {
  843. if (g_fStopCDOnExit) {
  844. if ( g_Devices[i]->State & CD_PLAYING
  845. || g_Devices[i]->State & CD_PAUSED ) {
  846. StopTheCdromDrive( i );
  847. }
  848. }
  849. #ifdef USE_IOCTLS
  850. if ( g_Devices[i]->hCd != NULL ) {
  851. CloseHandle( g_Devices[i]->hCd );
  852. }
  853. #else
  854. if ( g_Devices[i]->hCd != 0L ) {
  855. CloseCdRom( g_Devices[i]->hCd );
  856. g_Devices[i]->hCd = 0L;
  857. }
  858. #endif
  859. ErasePlayList(i);
  860. EraseSaveList(i);
  861. EraseTrackList(i);
  862. LocalFree( (HLOCAL) g_Devices[i] );
  863. }
  864. if (g_hBrushBkgd) {
  865. DeleteObject( g_hBrushBkgd );
  866. }
  867. WinHelp( hwnd, g_HelpFileName, HELP_QUIT, 0 );
  868. PostQuitMessage( 0 );
  869. }
  870. /******************************Public*Routine******************************\
  871. * CDPlay_OnClose
  872. *
  873. *
  874. *
  875. * History:
  876. * dd-mm-93 - StephenE - Created
  877. *
  878. \**************************************************************************/
  879. BOOL
  880. CDPlay_OnClose(
  881. HWND hwnd,
  882. BOOL fShuttingDown
  883. )
  884. {
  885. /*
  886. ** If we are playing or paused and the "don't stop playing
  887. ** on exit" flag set, then we need to tell the user that he is about
  888. ** to go into stupid mode. Basically CD Player can only perform as expected
  889. ** if the user has not mucked about with the play list, hasn't put the
  890. ** app into random mode or intro play mode or continuous play mode or
  891. ** multi-disc mode.
  892. */
  893. if ( !fShuttingDown && !g_fStopCDOnExit
  894. && (g_State & (CD_PLAYING | CD_PAUSED) ) ) {
  895. if ( !g_fSelectedOrder || g_fIntroPlay || g_fContinuous
  896. || !g_fSingleDisk || !PlayListMatchesAvailList() ) {
  897. TCHAR s1[256];
  898. TCHAR s2[256];
  899. int iMsgBoxRtn;
  900. _tcscpy( s1, IdStr( STR_EXIT_MESSAGE ) );
  901. _tcscpy( s2, IdStr( STR_CDPLAYER ) );
  902. iMsgBoxRtn = MessageBox( g_hwndApp, s1, s2,
  903. MB_APPLMODAL | MB_DEFBUTTON1 |
  904. MB_ICONQUESTION | MB_YESNO);
  905. if ( iMsgBoxRtn == IDNO ) {
  906. return TRUE;
  907. }
  908. }
  909. }
  910. //WriteSettings();
  911. return DestroyWindow( hwnd );
  912. }
  913. /*****************************Private*Routine******************************\
  914. * CDPlay_OnEndSession
  915. *
  916. * If the session is really ending make sure that we stop the CD Player
  917. * from playing and that all the ini file stuff is saved away.
  918. *
  919. * History:
  920. * dd-mm-93 - StephenE - Created
  921. *
  922. \**************************************************************************/
  923. void
  924. CDPlay_OnEndSession(
  925. HWND hwnd,
  926. BOOL fEnding
  927. )
  928. {
  929. if ( fEnding ) {
  930. CDPlay_OnClose( hwnd, fEnding );
  931. }
  932. }
  933. /******************************Public*Routine******************************\
  934. * CDPlay_OnSize
  935. *
  936. *
  937. *
  938. * History:
  939. * dd-mm-93 - StephenE - Created
  940. *
  941. \**************************************************************************/
  942. void
  943. CDPlay_OnSize(
  944. HWND hwnd,
  945. UINT state,
  946. int cx,
  947. int cy
  948. )
  949. {
  950. if (g_fIsIconic && (state != SIZE_MINIMIZED)) {
  951. SetWindowText( hwnd, IdStr( STR_CDPLAYER ) );
  952. }
  953. g_fIsIconic = (state == SIZE_MINIMIZED);
  954. SetWindowPos(GetDlgItem(g_hwndApp,IDC_LED),
  955. hwnd,
  956. 0,
  957. 0,
  958. cx,
  959. cy,
  960. SWP_NOACTIVATE);
  961. }
  962. /*
  963. * NormalizeNameForMenuDisplay
  964. This function turns a string like "Twist & Shout" into
  965. "Twist && Shout" because otherwise it will look like
  966. "Twist _Shout" in the menu due to the accelerator char
  967. */
  968. extern "C" void NormalizeNameForMenuDisplay(TCHAR* szInput, TCHAR* szOutput, DWORD cbLen)
  969. {
  970. ZeroMemory(szOutput,cbLen);
  971. WORD index1 = 0;
  972. WORD index2 = 0;
  973. for (; index1 < _tcslen(szInput); index1++)
  974. {
  975. szOutput[index2] = szInput[index1];
  976. if (szOutput[index2] == TEXT('&'))
  977. {
  978. szOutput[++index2] = TEXT('&');
  979. }
  980. index2++;
  981. }
  982. }
  983. /*****************************Private*Routine******************************\
  984. * CDPlay_OnNotify
  985. *
  986. * Time to display the little tool tips. Also, change the status bar
  987. * so that it displays a longer version of the tool tip text.
  988. *
  989. * History:
  990. * dd-mm-94 - StephenE - Created
  991. *
  992. \**************************************************************************/
  993. LRESULT
  994. CDPlay_OnNotify(
  995. HWND hwnd,
  996. int idFrom,
  997. NMHDR *pnmhdr
  998. )
  999. {
  1000. return TRUE;
  1001. }
  1002. BOOL
  1003. CDPlay_CopyData(
  1004. HWND hwnd,
  1005. PCOPYDATASTRUCT lpcpds
  1006. )
  1007. {
  1008. LPTSTR lpCmdLine;
  1009. // Make a copy of the passed command line as we are not supposed
  1010. // to write into the one passed in the WM_COPYDATA message.
  1011. lpCmdLine = (TCHAR*)AllocMemory( lpcpds->cbData );
  1012. _tcscpy( lpCmdLine, (LPCTSTR)lpcpds->lpData );
  1013. PostMessage (hwnd, WM_CDPLAYER_COPYDATA, 0, (LPARAM)(LPVOID)lpCmdLine);
  1014. return TRUE;
  1015. } // End CopyData
  1016. /*****************************Private*Routine******************************\
  1017. * CDPlay_OnCopyData
  1018. *
  1019. * Handles command lines passed from other intances of CD Player
  1020. *
  1021. * History:
  1022. * dd-mm-94 - StephenE - Created
  1023. *
  1024. \**************************************************************************/
  1025. BOOL
  1026. CDPlay_OnCopyData(
  1027. HWND hwnd,
  1028. PCOPYDATASTRUCT lpcpds
  1029. )
  1030. {
  1031. LPTSTR lpCmdLine;
  1032. BOOL fWasPlaying = FALSE;
  1033. BOOL fUpdate;
  1034. int iTrack = -1;
  1035. int iCdRom;
  1036. // Prevent Re-entrancy while
  1037. // we are opening/closing CD's
  1038. if (g_fInCopyData)
  1039. return FALSE;
  1040. g_fInCopyData = TRUE;
  1041. /*
  1042. ** Make a copy of the passed command line as we are not supposed
  1043. ** to write into the one passed in the WM_COPYDATA message.
  1044. */
  1045. //lpCmdLine = AllocMemory( lpcpds->cbData );
  1046. //_tcscpy( lpCmdLine, (LPCTSTR)lpcpds->lpData );
  1047. lpCmdLine = (LPTSTR)(LPVOID)lpcpds;
  1048. if (lpCmdLine == NULL)
  1049. {
  1050. g_fInCopyData = FALSE;
  1051. return 0L;
  1052. }
  1053. iCdRom = ParseCommandLine( lpCmdLine, &iTrack, FALSE );
  1054. if (iCdRom < 0 && iTrack < 0) {
  1055. LocalFree( (HLOCAL)lpCmdLine );
  1056. g_fInCopyData = FALSE;
  1057. return 0L;
  1058. }
  1059. // Check if it is just an update command?!?
  1060. fUpdate = IsUpdateOptionGiven (lpCmdLine);
  1061. if ((fUpdate) && (iTrack == -1))
  1062. {
  1063. if ((iCdRom >= 0) && (iCdRom < g_NumCdDevices))
  1064. {
  1065. CheckUnitCdrom(iCdRom, TRUE);
  1066. }
  1067. LocalFree( (HLOCAL)lpCmdLine );
  1068. g_fInCopyData = FALSE;
  1069. return 0L;
  1070. }
  1071. /*
  1072. ** Remember our current playing state as we need to temporarly
  1073. ** stop the CD if it is currently playing.
  1074. */
  1075. if ( g_State & (CD_PLAYING | CD_PAUSED) )
  1076. {
  1077. #ifdef DBG
  1078. dprintf(TEXT("Auto Stopping"));
  1079. #endif
  1080. while( !LockALLTableOfContents() )
  1081. {
  1082. MSG msg;
  1083. #if DBG
  1084. dprintf(TEXT("Busy waiting for TOC to become valid!"));
  1085. #endif
  1086. GetMessage( &msg, NULL, WM_NOTIFY_TOC_READ, WM_NOTIFY_TOC_READ );
  1087. DispatchMessage( &msg );
  1088. }
  1089. CdPlayerStopCmd();
  1090. fWasPlaying = TRUE;
  1091. }
  1092. /*
  1093. ** Figure what has been passed and act on it accordingly.
  1094. */
  1095. HandlePassedCommandLine( lpCmdLine, FALSE );
  1096. /*
  1097. ** If we were playing make sure that we are still playing the
  1098. ** new track(s)
  1099. */
  1100. if ( fWasPlaying || g_fPlay )
  1101. {
  1102. #ifdef DBG
  1103. dprintf(TEXT("Trying to autoplay"));
  1104. #endif
  1105. while( !LockTableOfContents(g_CurrCdrom) )
  1106. {
  1107. MSG msg;
  1108. #ifdef DBG
  1109. dprintf(TEXT("Busy waiting for TOC to become valid!"));
  1110. #endif
  1111. GetMessage( &msg, NULL, WM_NOTIFY_TOC_READ, WM_NOTIFY_TOC_READ );
  1112. DispatchMessage( &msg );
  1113. }
  1114. CdPlayerPlayCmd();
  1115. }
  1116. /*
  1117. ** Free the local copy of the command line.
  1118. */
  1119. LocalFree( (HLOCAL)lpCmdLine );
  1120. g_fInCopyData = FALSE;
  1121. return 0L;
  1122. }
  1123. /*****************************Private*Routine******************************\
  1124. * CDPlay_OnTocRead
  1125. *
  1126. *
  1127. *
  1128. * History:
  1129. * dd-mm-94 - StephenE - Created
  1130. *
  1131. \**************************************************************************/
  1132. BOOL
  1133. CDPlay_OnTocRead(
  1134. int iDriveRead
  1135. )
  1136. {
  1137. static int iNumRead = 0;
  1138. // This serializes processing between this
  1139. // function and the various Table of Content Threads
  1140. // Preventing resource contention on CDROM Multi-Changers.
  1141. EnterCriticalSection (&g_csTOCSerialize);
  1142. /*
  1143. ** Have we finished the initial read of the CD-Rom TOCs ?
  1144. ** If so we have to re-open the device. We only need to do this
  1145. ** on Daytona because MCI device handles are not shared between threads.
  1146. */
  1147. iNumRead++;
  1148. #ifndef USE_IOCTLS
  1149. OSVERSIONINFO os;
  1150. os.dwOSVersionInfoSize = sizeof(os);
  1151. GetVersionEx(&os);
  1152. if (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
  1153. {
  1154. if (iNumRead <= g_NumCdDevices) {
  1155. /*
  1156. ** Now, open the cdrom device on the UI thread.
  1157. */
  1158. g_Devices[iDriveRead]->hCd =
  1159. OpenCdRom( g_Devices[iDriveRead]->drive, NULL );
  1160. }
  1161. }
  1162. #endif
  1163. /*
  1164. ** This means that one of the threads dedicated to reading the
  1165. ** toc has finished. iDriveRead contains the relevant cdrom id.
  1166. */
  1167. LockALLTableOfContents();
  1168. if ( g_Devices[iDriveRead]->State & CD_LOADED )
  1169. {
  1170. /*
  1171. ** We have a CD loaded, so generate unique ID
  1172. ** based on TOC information.
  1173. */
  1174. g_Devices[iDriveRead]->CdInfo.Id = ComputeNewDiscId( iDriveRead );
  1175. /*
  1176. ** Check database for this compact disc
  1177. */
  1178. AddFindEntry( iDriveRead, g_Devices[iDriveRead]->CdInfo.Id,
  1179. &(g_Devices[iDriveRead]->toc) );
  1180. //plop this into the punit table
  1181. //try to find the drive in the unit table
  1182. if (g_pSink)
  1183. {
  1184. LPCDOPT pOpt = (LPCDOPT)g_pSink->GetOptions();
  1185. LPCDOPTIONS pCDOpts = NULL;
  1186. LPCDUNIT pUnit = NULL;
  1187. if (pOpt)
  1188. {
  1189. pCDOpts = pOpt->GetCDOpts();
  1190. }
  1191. if (pCDOpts)
  1192. {
  1193. pUnit = pCDOpts->pCDUnitList;
  1194. }
  1195. //scan the list to find the one we want
  1196. for (int index = 0; index < iDriveRead; index++)
  1197. {
  1198. if (pUnit)
  1199. {
  1200. pUnit = pUnit->pNext;
  1201. }
  1202. }
  1203. if (pUnit)
  1204. {
  1205. pUnit->dwTitleID = g_Devices[iDriveRead]->CdInfo.Id;
  1206. pUnit->dwNumTracks = g_Devices[iDriveRead]->CdInfo.NumTracks;
  1207. GetTOC(iDriveRead,pUnit->szNetQuery);
  1208. pOpt->DiscChanged(pUnit);
  1209. }
  1210. } //end if gpsink
  1211. }
  1212. /*
  1213. ** If we have completed the initialization of the Cd-Rom drives we can
  1214. ** now complete the startup processing of the application.
  1215. */
  1216. if (iNumRead == g_NumCdDevices)
  1217. {
  1218. CompleteCdPlayerStartUp();
  1219. }
  1220. else {
  1221. /*
  1222. ** if we are in random mode, then we need to shuffle the play lists.
  1223. ** but only if we can lock all the cd devices.
  1224. */
  1225. TimeAdjustInitialize( iDriveRead );
  1226. if ( g_fSelectedOrder == FALSE ) {
  1227. if ( LockALLTableOfContents() ) {
  1228. ComputeAndUseShufflePlayLists();
  1229. }
  1230. }
  1231. ComputeDriveComboBox();
  1232. if (iDriveRead == g_CurrCdrom)
  1233. {
  1234. if (g_fPlay)
  1235. {
  1236. CdPlayerPlayCmd();
  1237. }
  1238. SetPlayButtonsEnableState();
  1239. }
  1240. }
  1241. LeaveCriticalSection (&g_csTOCSerialize);
  1242. return TRUE;
  1243. }
  1244. /*****************************Private*Routine******************************\
  1245. * CDPlay_OnDeviceChange
  1246. *
  1247. *
  1248. *
  1249. * History:
  1250. * dd-mm-94 - StephenE - Created
  1251. *
  1252. \**************************************************************************/
  1253. BOOL
  1254. CDPlay_OnDeviceChange(
  1255. HWND hwnd,
  1256. WPARAM wParam,
  1257. LPARAM lParam)
  1258. {
  1259. UINT uiEvent = (UINT)wParam;
  1260. DWORD_PTR dwData = (DWORD_PTR)lParam;
  1261. switch (uiEvent)
  1262. {
  1263. case DBT_DEVICEARRIVAL: // Insertion
  1264. case DBT_DEVICEREMOVECOMPLETE: // Ejection
  1265. if ((PDEV_BROADCAST_HDR)dwData)
  1266. {
  1267. switch (((PDEV_BROADCAST_HDR)dwData)->dbch_devicetype)
  1268. {
  1269. case DBT_DEVTYP_VOLUME:
  1270. {
  1271. TCHAR chDrive[4] = TEXT("A:\\");
  1272. INT i,j,drive;
  1273. DWORD dwCurr;
  1274. PDEV_BROADCAST_VOLUME pdbv;
  1275. DWORD dwMask, dwDrives;
  1276. pdbv = (PDEV_BROADCAST_VOLUME)dwData;
  1277. dwMask = pdbv->dbcv_unitmask;
  1278. dwDrives = GetLogicalDrives();
  1279. dwMask &= dwDrives;
  1280. if (dwMask)
  1281. {
  1282. // Check all drives for match
  1283. for (i = 0; i < 32; i++)
  1284. {
  1285. dwCurr = 1 << i;
  1286. if (dwCurr & dwMask)
  1287. {
  1288. // Check drive
  1289. chDrive[0] = TEXT('A') + i;
  1290. if ( GetDriveType(chDrive) == DRIVE_CDROM )
  1291. {
  1292. // Find Associated Drive structure
  1293. drive = -1;
  1294. for (j = 0; j < g_NumCdDevices; j++)
  1295. {
  1296. if (g_Devices[j]->drive == chDrive[0])
  1297. drive = j;
  1298. }
  1299. // Structure not found, make one
  1300. if (drive == -1)
  1301. {
  1302. #ifdef DBG
  1303. dprintf (TEXT("CDPlay_OnDeviceChange - didn't find drive"));
  1304. #endif
  1305. if (g_NumCdDevices > MAX_CD_DEVICES)
  1306. {
  1307. // Error - not enough device slots
  1308. break;
  1309. }
  1310. g_Devices[g_NumCdDevices] = (CDROM*)AllocMemory( sizeof(CDROM) );
  1311. if (NULL == g_Devices[g_NumCdDevices])
  1312. {
  1313. // Error - unable to get enough memory
  1314. break;
  1315. }
  1316. g_Devices[g_NumCdDevices]->drive = chDrive[0];
  1317. drive = g_NumCdDevices;
  1318. g_NumCdDevices++;
  1319. }
  1320. // Insert/Eject new drive
  1321. if (uiEvent == DBT_DEVICEARRIVAL)
  1322. {
  1323. // Drive has been inserted
  1324. // The Shell should inform us using
  1325. // the AUTOPLAY through WM_COPYDDATA
  1326. //This is only necessary to detect discs with
  1327. //more than just redbook audio on them ...
  1328. //to prevent a double-scan of any discs that
  1329. //are normal audio, we need to block the "get it now"
  1330. //net prompt when scanning this way
  1331. g_fBlockNetPrompt = TRUE;
  1332. CheckUnitCdrom(drive,TRUE);
  1333. g_fBlockNetPrompt = FALSE;
  1334. return FALSE;
  1335. }
  1336. else
  1337. {
  1338. NoMediaUpdate (drive);
  1339. }
  1340. }
  1341. }
  1342. }
  1343. }
  1344. break;
  1345. }
  1346. default:
  1347. // Not a logical volume message
  1348. break;
  1349. }
  1350. }
  1351. break;
  1352. case DBT_DEVICEQUERYREMOVE: // Permission to remove a device is requested.
  1353. case DBT_DEVICEQUERYREMOVEFAILED: // Request to remove a device has been canceled.
  1354. case DBT_DEVICEREMOVEPENDING: // Device is about to be removed. Can not be denied.
  1355. case DBT_DEVICETYPESPECIFIC: // Device-specific event.
  1356. case DBT_CONFIGCHANGED: // Current configuration has changed.
  1357. default:
  1358. break;
  1359. }
  1360. return TRUE;
  1361. } // End CDPlay_OnDeviceChange
  1362. /*****************************Private*Routine******************************\
  1363. * CDPlay_OnDropFiles
  1364. *
  1365. *
  1366. *
  1367. * History:
  1368. * dd-mm-94 - StephenE - Created
  1369. *
  1370. \**************************************************************************/
  1371. void
  1372. CDPlay_OnDropFiles(
  1373. HWND hwnd,
  1374. HDROP hdrop
  1375. )
  1376. {
  1377. int cFiles;
  1378. int cGoodFiles;
  1379. int iTextLen;
  1380. int i;
  1381. TCHAR szFileName[MAX_PATH+3];
  1382. LPTSTR lpCommandLine;
  1383. BOOL fWasPlaying = FALSE;
  1384. // Prevent Re-entrancy while we are
  1385. // Opening and closing CD's
  1386. if (g_fInCopyData)
  1387. return;
  1388. g_fInCopyData = TRUE;
  1389. /*
  1390. ** Determine how many files were passed to us.
  1391. */
  1392. cFiles = DragQueryFile( hdrop, (UINT)-1, (LPTSTR)NULL, 0 );
  1393. /*
  1394. ** Calculate the length of the command line each filename should be
  1395. ** separated by a space character
  1396. */
  1397. iTextLen = _tcslen( g_szCdplayer );
  1398. iTextLen += _tcslen( g_szPlayOption );
  1399. iTextLen += _tcslen( g_szTrackOption );
  1400. for ( cGoodFiles = cFiles, i = 0; i < cFiles; i++ ) {
  1401. int unused1, unused2;
  1402. DragQueryFile( hdrop, i, szFileName, MAX_PATH );
  1403. if (IsTrackFileNameValid( szFileName, &unused1,
  1404. &unused2, TRUE, FALSE )) {
  1405. // Add on 3 extra characters - one for the space and
  1406. // two for quote marks, we do this because the filename
  1407. // given may contain space characters.
  1408. iTextLen += _tcslen( szFileName ) + 2 + 1;
  1409. }
  1410. else {
  1411. cGoodFiles--;
  1412. }
  1413. }
  1414. /*
  1415. ** If the none of the dropped files are valid tracks just return
  1416. */
  1417. if (cGoodFiles < 1) {
  1418. g_fInCopyData = FALSE;
  1419. return;
  1420. }
  1421. /*
  1422. ** Allocate a chunk of memory big enough for all the options and
  1423. ** filenames. Don't forget the NULL.
  1424. */
  1425. lpCommandLine = (TCHAR*)AllocMemory(sizeof(TCHAR) * (iTextLen + 1));
  1426. /*
  1427. ** Add a dummy intial command line arg. This is because the
  1428. ** first arg is always the name of the invoked application. We ignore
  1429. ** this paramter. Also if we are currently playing we need to
  1430. ** add the -play option to command line as well as stop the CD.
  1431. */
  1432. _tcscpy( lpCommandLine, g_szCdplayer );
  1433. if ( g_State & (CD_PLAYING | CD_PAUSED) ) {
  1434. CdPlayerStopCmd();
  1435. fWasPlaying = TRUE;
  1436. _tcscat( lpCommandLine, g_szPlayOption );
  1437. }
  1438. /*
  1439. ** If there is more than one file name specified then we should constuct
  1440. ** a new playlist from the given files.
  1441. */
  1442. if ( cGoodFiles > 1) {
  1443. _tcscat( lpCommandLine, g_szTrackOption );
  1444. }
  1445. /*
  1446. ** Build up the command line.
  1447. */
  1448. for ( i = 0; i < cFiles; i++ ) {
  1449. int unused1, unused2;
  1450. DragQueryFile( hdrop, i, szFileName, MAX_PATH );
  1451. if (IsTrackFileNameValid( szFileName, &unused1,
  1452. &unused2, TRUE, TRUE )) {
  1453. _tcscat( lpCommandLine, TEXT("\'") );
  1454. _tcscat( lpCommandLine, szFileName );
  1455. _tcscat( lpCommandLine, TEXT("\'") );
  1456. _tcscat( lpCommandLine, g_szBlank );
  1457. }
  1458. }
  1459. /*
  1460. ** now process the newly constructed command line.
  1461. */
  1462. HandlePassedCommandLine( lpCommandLine, FALSE );
  1463. /*
  1464. ** If we were playing make sure that we are still playing the
  1465. ** new track(s)
  1466. */
  1467. if ( fWasPlaying ) {
  1468. CdPlayerPlayCmd();
  1469. }
  1470. LocalFree( lpCommandLine );
  1471. DragFinish( hdrop );
  1472. g_fInCopyData = FALSE;
  1473. }
  1474. /*****************************Private*Routine******************************\
  1475. * ResolveLink
  1476. *
  1477. * Takes the shortcut (shell link) file pointed to be szFileName and
  1478. * resolves the link returning the linked file name in szFileName.
  1479. *
  1480. * szFileName must point to at least MAX_PATH amount of TCHARS. The function
  1481. * return TRUE if the link was successfully resolved and FALSE otherwise.
  1482. *
  1483. * History:
  1484. * dd-mm-94 - StephenE - Created
  1485. * 03-11-95 - ShawnB - Unicode enabled
  1486. *
  1487. \**************************************************************************/
  1488. BOOL
  1489. ResolveLink(
  1490. TCHAR *szFileName
  1491. )
  1492. {
  1493. return FALSE;
  1494. }
  1495. //#endif
  1496. /******************************Public*Routine******************************\
  1497. * FatalApplicationError
  1498. *
  1499. * Call this function if something "bad" happens to the application. It
  1500. * displays an error message and then kills itself.
  1501. *
  1502. * History:
  1503. * 18-11-93 - StephenE - Created
  1504. *
  1505. \**************************************************************************/
  1506. void
  1507. FatalApplicationError(
  1508. INT uIdStringResource,
  1509. ...
  1510. )
  1511. {
  1512. va_list va;
  1513. TCHAR chBuffer1[ STR_MAX_STRING_LEN ];
  1514. TCHAR chBuffer2[ STR_MAX_STRING_LEN ];
  1515. /*
  1516. ** Load the relevant messages
  1517. */
  1518. va_start(va, uIdStringResource);
  1519. wvsprintf(chBuffer1, IdStr(uIdStringResource), va);
  1520. va_end(va);
  1521. _tcscpy( chBuffer2, IdStr(STR_FATAL_ERROR) ); /*"CD Player: Fatal Error"*/
  1522. /*
  1523. ** How much of the application do we need to kill
  1524. */
  1525. if (g_hwndApp) {
  1526. if ( IsWindowVisible(g_hwndApp) ) {
  1527. BringWindowToTop(g_hwndApp);
  1528. }
  1529. MessageBox( g_hwndApp, chBuffer1, chBuffer2,
  1530. MB_ICONSTOP | MB_OK | MB_APPLMODAL | MB_SETFOREGROUND );
  1531. DestroyWindow( g_hwndApp );
  1532. }
  1533. else {
  1534. MessageBox( NULL, chBuffer1, chBuffer2,
  1535. MB_APPLMODAL | MB_ICONSTOP | MB_OK | MB_SETFOREGROUND );
  1536. }
  1537. ExitProcess( (UINT)-1 );
  1538. }
  1539. /******************************Public*Routine******************************\
  1540. * IdStr
  1541. *
  1542. * Loads the given string resource ID into the passed storage.
  1543. *
  1544. * History:
  1545. * 18-11-93 - StephenE - Created
  1546. *
  1547. \**************************************************************************/
  1548. LPTSTR
  1549. IdStr(
  1550. int idResource
  1551. )
  1552. {
  1553. static TCHAR chBuffer[ STR_MAX_STRING_LEN ];
  1554. if (LoadString(g_hInst, idResource, chBuffer, STR_MAX_STRING_LEN) == 0)
  1555. {
  1556. return TEXT("");
  1557. }
  1558. return chBuffer;
  1559. }
  1560. /******************************Public*Routine******************************\
  1561. * CheckMenuItemIfTrue
  1562. *
  1563. * If "flag" TRUE the given menu item is checked, otherwise it is unchecked.
  1564. *
  1565. * History:
  1566. * 18-11-93 - StephenE - Created
  1567. *
  1568. \**************************************************************************/
  1569. void
  1570. CheckMenuItemIfTrue(
  1571. HMENU hMenu,
  1572. UINT idItem,
  1573. BOOL flag
  1574. )
  1575. {
  1576. UINT uFlags;
  1577. if (flag) {
  1578. uFlags = MF_CHECKED | MF_BYCOMMAND;
  1579. }
  1580. else {
  1581. uFlags = MF_UNCHECKED | MF_BYCOMMAND;
  1582. }
  1583. CheckMenuItem( hMenu, idItem, uFlags );
  1584. }
  1585. /******************************Public*Routine******************************\
  1586. * ReadSettings
  1587. *
  1588. * Read app settings from ini file.
  1589. *
  1590. * History:
  1591. * 18-11-93 - StephenE - Created
  1592. *
  1593. \**************************************************************************/
  1594. void
  1595. ReadSettings(
  1596. void* pData
  1597. )
  1598. {
  1599. LPCDOPT pOpt = NULL;
  1600. LPCDOPTDATA pOptionData = (LPCDOPTDATA)pData;
  1601. //if no option data, get some!
  1602. if (pOptionData == NULL)
  1603. {
  1604. pOpt = (LPCDOPT)g_pSink->GetOptions();
  1605. if( pOpt )
  1606. {
  1607. LPCDOPTIONS pOptions = pOpt->GetCDOpts();
  1608. pOptionData = pOptions->pCDData;
  1609. }
  1610. }
  1611. //if we still don't have it, bail out!
  1612. if (pOptionData == NULL)
  1613. {
  1614. return;
  1615. }
  1616. g_fStopCDOnExit = pOptionData->fExitStop;
  1617. //if being called because of user dialog setting, reset the play mode flag
  1618. if (pData != NULL)
  1619. {
  1620. g_fPlay = pOptionData->fStartPlay;
  1621. }
  1622. if ( g_NumCdDevices < 2 )
  1623. {
  1624. g_fMultiDiskAvailable = FALSE;
  1625. g_fSingleDisk = TRUE;
  1626. }
  1627. else {
  1628. g_fMultiDiskAvailable = TRUE;
  1629. g_fSingleDisk = FALSE;
  1630. }
  1631. g_fDisplayD = FALSE;
  1632. g_fDisplayDr = FALSE;
  1633. g_fDisplayT = FALSE;
  1634. g_fDisplayTr = FALSE;
  1635. switch (pOptionData->fDispMode)
  1636. {
  1637. case CDDISP_CDTIME :
  1638. {
  1639. g_fDisplayD = TRUE;
  1640. }
  1641. break;
  1642. case CDDISP_CDREMAIN :
  1643. {
  1644. g_fDisplayDr = TRUE;
  1645. }
  1646. break;
  1647. case CDDISP_TRACKTIME :
  1648. {
  1649. g_fDisplayT = TRUE;
  1650. }
  1651. break;
  1652. case CDDISP_TRACKREMAIN :
  1653. {
  1654. g_fDisplayTr = TRUE;
  1655. }
  1656. break;
  1657. }
  1658. g_IntroPlayLength = pOptionData->dwIntroTime;
  1659. //set into correct mode
  1660. g_fSelectedOrder = (pOptionData->dwPlayMode != IDM_MODE_RANDOM);
  1661. //if not selected order (i.e. we're in random mode) then also make it continuous.
  1662. if (!g_fSelectedOrder)
  1663. {
  1664. g_fContinuous = TRUE;
  1665. }
  1666. /*
  1667. ** Make sure that the LED display format is correct
  1668. */
  1669. if ( g_fDisplayT == FALSE && g_fDisplayTr == FALSE
  1670. && g_fDisplayDr == FALSE && g_fDisplayD == FALSE)
  1671. {
  1672. g_fDisplayT = TRUE;
  1673. }
  1674. }
  1675. /******************************Public*Routine******************************\
  1676. * LockTableOfContents
  1677. *
  1678. * This function is used to determine if it is valid for the UI thread
  1679. * to access the table of contents for the specified CD Rom. If this
  1680. * function returns FALSE the UI thread should NOT touch the table of
  1681. * contents for this CD Rom.
  1682. *
  1683. * History:
  1684. * 18-11-93 - StephenE - Created
  1685. *
  1686. \**************************************************************************/
  1687. BOOL
  1688. LockTableOfContents(
  1689. int cdrom
  1690. )
  1691. {
  1692. DWORD dwRet;
  1693. if (g_Devices[cdrom]->fIsTocValid) {
  1694. return TRUE;
  1695. }
  1696. if (g_Devices[cdrom]->hThreadToc == NULL) {
  1697. return FALSE;
  1698. }
  1699. dwRet = WaitForSingleObject(g_Devices[cdrom]->hThreadToc, 0L );
  1700. if (dwRet == WAIT_OBJECT_0) {
  1701. GetExitCodeThread( g_Devices[cdrom]->hThreadToc, &dwRet );
  1702. g_Devices[cdrom]->fIsTocValid = (BOOL)dwRet;
  1703. CloseHandle( g_Devices[cdrom]->hThreadToc );
  1704. g_Devices[cdrom]->hThreadToc = NULL;
  1705. }
  1706. return g_Devices[cdrom]->fIsTocValid;
  1707. }
  1708. /******************************Public*Routine******************************\
  1709. * LockAllTableOfContents
  1710. *
  1711. * This function is used to determine if it is valid for the UI thread
  1712. * to access the table of contents for the ALL the cdroms devices.
  1713. * The function returns FALSE the UI thread should NOT touch the table of
  1714. * contents for any CD Rom.
  1715. *
  1716. * History:
  1717. * 18-11-93 - StephenE - Created
  1718. *
  1719. \**************************************************************************/
  1720. BOOL
  1721. LockALLTableOfContents(
  1722. void
  1723. )
  1724. {
  1725. BOOL fLock;
  1726. int i;
  1727. for (i = 0, fLock = TRUE; fLock && (i < g_NumCdDevices); i++) {
  1728. fLock = LockTableOfContents(i);
  1729. }
  1730. return fLock;
  1731. }
  1732. /******************************Public*Routine******************************\
  1733. * AllocMemory
  1734. *
  1735. * Allocates a memory of the given size. This function will terminate the
  1736. * application if the allocation failed. Memory allocated by this function
  1737. * must be freed with LocalFree. The memory should not be locked or unlocked.
  1738. *
  1739. * History:
  1740. * 18-11-93 - StephenE - Created
  1741. *
  1742. \**************************************************************************/
  1743. LPVOID
  1744. AllocMemory(
  1745. UINT uSize
  1746. )
  1747. {
  1748. LPVOID lp;
  1749. lp = LocalAlloc( LPTR, uSize );
  1750. if (lp == NULL) {
  1751. /*
  1752. ** No memory - no application, simple !
  1753. */
  1754. FatalApplicationError( STR_FAIL_INIT );
  1755. }
  1756. return lp;
  1757. }
  1758. /******************************Public*Routine******************************\
  1759. * SetPlayButtonsEnableState
  1760. *
  1761. * Sets the play buttons enable state to match the state of the current
  1762. * cdrom device. See below...
  1763. *
  1764. *
  1765. * CDPlayer buttons enable state table
  1766. * Ŀ
  1767. * E=Enabled D=Disabled Play Pause Eject Stop Other DataB
  1768. * Ĵ
  1769. * Disk in use D D D D D D
  1770. * Ĵ
  1771. * No music cd or data cdrom D D E D D D
  1772. * Ĵ
  1773. * Music cd (playing) D E E E E E
  1774. * Ĵ
  1775. * Music cd (paused) E E E E E E
  1776. * Ĵ
  1777. * Music cd (stopped) E D E D E E
  1778. *
  1779. *
  1780. *
  1781. * History:
  1782. * 18-11-93 - StephenE - Created
  1783. *
  1784. \**************************************************************************/
  1785. void
  1786. SetPlayButtonsEnableState(
  1787. void
  1788. )
  1789. {
  1790. BOOL fMusicCdLoaded;
  1791. BOOL fActivePlayList;
  1792. int i;
  1793. /*
  1794. ** Do we have a music cd loaded.
  1795. */
  1796. if (g_State & (CD_BEING_SCANNED | CD_NO_CD | CD_DATA_CD_LOADED | CD_IN_USE))
  1797. {
  1798. fMusicCdLoaded = FALSE;
  1799. }
  1800. else
  1801. {
  1802. fMusicCdLoaded = TRUE;
  1803. }
  1804. /*
  1805. ** Is there an active playlist
  1806. */
  1807. if ( (CDTIME(g_CurrCdrom).TotalMin == 0) && (CDTIME(g_CurrCdrom).TotalSec == 0) )
  1808. {
  1809. fActivePlayList = FALSE;
  1810. }
  1811. else
  1812. {
  1813. fActivePlayList = TRUE;
  1814. }
  1815. //tell the main UI about the track button
  1816. HWND hwndTrackButton = GetDlgItem(GetParent(g_hwndApp),IDB_TRACK);
  1817. if (hwndTrackButton)
  1818. {
  1819. EnableWindow(hwndTrackButton,(fMusicCdLoaded & fActivePlayList));
  1820. }
  1821. //just turn off all "old" cdplayer buttons, since they are not used in this app
  1822. EnableWindow( g_hwndControls[INDEX(IDM_PLAYBAR_PLAY)], FALSE );
  1823. EnableWindow( g_hwndControls[INDEX(IDM_PLAYBAR_STOP)], FALSE );
  1824. EnableWindow( g_hwndControls[INDEX(IDM_PLAYBAR_PAUSE)], FALSE );
  1825. /*
  1826. ** Do the remaining buttons
  1827. */
  1828. for ( i = IDM_PLAYBAR_PREVTRACK; i <= IDM_PLAYBAR_NEXTTRACK; i++ )
  1829. {
  1830. EnableWindow( g_hwndControls[INDEX(i)], FALSE );
  1831. }
  1832. /*
  1833. ** If the drive is in use then we must diable the eject button.
  1834. */
  1835. EnableWindow( g_hwndControls[INDEX(IDM_PLAYBAR_EJECT)], FALSE );
  1836. }
  1837. /******************************Public*Routine******************************\
  1838. * HeartBeatTimerProc
  1839. *
  1840. * This function is responsible for.
  1841. *
  1842. * 1. detecting new or ejected cdroms.
  1843. * 2. flashing the LED display if we are in paused mode.
  1844. * 3. Incrementing the LED display if we are in play mode.
  1845. *
  1846. * History:
  1847. * 18-11-93 - StephenE - Created
  1848. *
  1849. \**************************************************************************/
  1850. void CALLBACK
  1851. HeartBeatTimerProc(
  1852. HWND hwnd,
  1853. UINT uMsg,
  1854. UINT idEvent,
  1855. DWORD dwTime
  1856. )
  1857. {
  1858. static DWORD dwTickCount;
  1859. DWORD dwMod;
  1860. ++dwTickCount;
  1861. dwMod = (dwTickCount % 6);
  1862. /*
  1863. ** Check for "letting go" of drive every 3 seconds
  1864. */
  1865. if ( 0 == dwMod )
  1866. {
  1867. for (int i = 0; i < g_NumCdDevices; i++)
  1868. {
  1869. if ( (!(g_Devices[i]->State & CD_EDITING))
  1870. && (!(g_Devices[i]->State & CD_PLAYING)) )
  1871. {
  1872. CheckUnitCdrom(i,FALSE);
  1873. }
  1874. }
  1875. }
  1876. if ( g_State & CD_PLAYING ) {
  1877. if ( LockALLTableOfContents() ) {
  1878. SyncDisplay();
  1879. }
  1880. }
  1881. /*
  1882. ** If we are paused and NOT skipping flash the display.
  1883. */
  1884. else if ((g_State & CD_PAUSED) && !(g_State & CD_SEEKING)) {
  1885. HWND hwnd;
  1886. switch ( dwMod ) {
  1887. case 2:
  1888. case 5:
  1889. case 8:
  1890. case 11:
  1891. if ( g_fIsIconic ) {
  1892. //Next two lines removed to fix tooltip bug:<mwetzel 08.26.97>
  1893. //SetWindowText( g_hwndApp, g_szBlank );
  1894. //UpdateWindow( g_hwndApp );
  1895. }
  1896. else {
  1897. hwnd = g_hwndControls[INDEX(IDC_LED)];
  1898. g_fFlashLed = TRUE;
  1899. //SetWindowText( hwnd, g_szBlank );
  1900. g_fFlashLed = FALSE;
  1901. }
  1902. break;
  1903. case 0:
  1904. case 3:
  1905. case 6:
  1906. case 9:
  1907. UpdateDisplay( DISPLAY_UPD_LED );
  1908. break;
  1909. }
  1910. }
  1911. }
  1912. /******************************Public*Routine******************************\
  1913. * SkipBeatTimerProc
  1914. *
  1915. * This function is responsible for advancing or retreating the current
  1916. * playing position.
  1917. *
  1918. *
  1919. * History:
  1920. * 18-11-93 - StephenE - Created
  1921. *
  1922. \**************************************************************************/
  1923. void CALLBACK
  1924. SkipBeatTimerProc(
  1925. HWND hwnd,
  1926. UINT uMsg,
  1927. UINT idEvent,
  1928. DWORD dwTime
  1929. )
  1930. {
  1931. /*
  1932. ** Deteremine if it is time to accelerate the skipping frequency.
  1933. */
  1934. switch (++g_AcceleratorCount) {
  1935. case SKIP_ACCELERATOR_LIMIT1:
  1936. KillTimer( hwnd, idEvent );
  1937. SetTimer( hwnd, idEvent, SKIPBEAT_TIMER_RATE2, (TIMERPROC)SkipBeatTimerProc );
  1938. break;
  1939. case SKIP_ACCELERATOR_LIMIT2:
  1940. KillTimer( hwnd, idEvent );
  1941. SetTimer( hwnd, idEvent, SKIPBEAT_TIMER_RATE3, (TIMERPROC)SkipBeatTimerProc );
  1942. break;
  1943. }
  1944. if ( LockALLTableOfContents() ) {
  1945. if ( idEvent == IDM_PLAYBAR_SKIPFORE) {
  1946. TimeAdjustIncSecond( g_CurrCdrom );
  1947. /*
  1948. ** When TimeAjustIncSecond gets to the end of the last track
  1949. ** it sets CURRTRACK(g_CurrCdrom) equal to NULL. When this
  1950. ** occurs we effectively reset the CD Player
  1951. */
  1952. if ( CURRTRACK(g_CurrCdrom) == NULL ) {
  1953. if ( g_State & (CD_WAS_PLAYING | CD_PAUSED) ) {
  1954. SendMessage( g_hwndControls[INDEX(IDM_PLAYBAR_STOP)],
  1955. WM_LBUTTONDOWN, 0, 0L );
  1956. SendMessage( g_hwndControls[INDEX(IDM_PLAYBAR_STOP)],
  1957. WM_LBUTTONUP, 0, 0L );
  1958. }
  1959. else {
  1960. /*
  1961. ** Seek to the first playable track.
  1962. */
  1963. CURRTRACK(g_CurrCdrom) = FindFirstTrack( g_CurrCdrom );
  1964. if ( CURRTRACK(g_CurrCdrom) != NULL ) {
  1965. TimeAdjustSkipToTrack( g_CurrCdrom,
  1966. CURRTRACK(g_CurrCdrom) );
  1967. UpdateDisplay( DISPLAY_UPD_LED | DISPLAY_UPD_TRACK_TIME |
  1968. DISPLAY_UPD_TRACK_NAME );
  1969. SetPlayButtonsEnableState();
  1970. }
  1971. }
  1972. }
  1973. }
  1974. else {
  1975. TimeAdjustDecSecond( g_CurrCdrom );
  1976. }
  1977. }
  1978. }
  1979. /******************************Public*Routine******************************\
  1980. * UpdateDisplay
  1981. *
  1982. * This routine updates the display according to the flags that
  1983. * are passed in. The display consists of the LED display, the
  1984. * track and title names, the disc and track lengths and the cdrom
  1985. * combo-box.
  1986. *
  1987. * History:
  1988. * 18-11-93 - StephenE - Created
  1989. *
  1990. \**************************************************************************/
  1991. void
  1992. UpdateDisplay(
  1993. DWORD Flags
  1994. )
  1995. {
  1996. TCHAR lpsz[55];
  1997. TCHAR lpszIcon[75];
  1998. PTRACK_PLAY tr;
  1999. int track;
  2000. int mtemp, stemp, m, s;
  2001. /*
  2002. ** Check for valid flags
  2003. */
  2004. if ( Flags == 0 ) {
  2005. return;
  2006. }
  2007. ZeroMemory(lpsz,sizeof(lpsz));
  2008. /*
  2009. ** Grab current track information
  2010. */
  2011. if (CURRTRACK(g_CurrCdrom) != NULL) {
  2012. track = CURRTRACK(g_CurrCdrom)->TocIndex + FIRSTTRACK(g_CurrCdrom);
  2013. }
  2014. else {
  2015. track = 0;
  2016. }
  2017. /*
  2018. ** Update the LED box?
  2019. */
  2020. if (Flags & DISPLAY_UPD_LED) {
  2021. /*
  2022. ** Update LED box
  2023. */
  2024. if (g_fDisplayT) {
  2025. if (Flags & DISPLAY_UPD_LEADOUT_TIME) {
  2026. wsprintf( lpsz, TRACK_TIME_LEADOUT_FORMAT,
  2027. //track,
  2028. CDTIME(g_CurrCdrom).TrackCurMin,
  2029. g_szTimeSep,
  2030. CDTIME(g_CurrCdrom).TrackCurSec );
  2031. }
  2032. else {
  2033. wsprintf( lpsz, TRACK_TIME_FORMAT,
  2034. //track,
  2035. CDTIME(g_CurrCdrom).TrackCurMin,
  2036. g_szTimeSep,
  2037. CDTIME(g_CurrCdrom).TrackCurSec );
  2038. }
  2039. }
  2040. if (g_fDisplayTr) {
  2041. if (Flags & DISPLAY_UPD_LEADOUT_TIME) {
  2042. wsprintf( lpsz, TRACK_REM_FORMAT, //track - 1,
  2043. CDTIME(g_CurrCdrom).TrackCurMin,
  2044. g_szTimeSep,
  2045. CDTIME(g_CurrCdrom).TrackCurSec );
  2046. }
  2047. else {
  2048. wsprintf( lpsz, TRACK_REM_FORMAT, //track,
  2049. CDTIME(g_CurrCdrom).TrackRemMin,
  2050. g_szTimeSep,
  2051. CDTIME(g_CurrCdrom).TrackRemSec );
  2052. }
  2053. }
  2054. if (g_fDisplayD)
  2055. {
  2056. /*
  2057. ** Compute remaining time, then sub from total time
  2058. */
  2059. mtemp = stemp = m = s =0;
  2060. if (CURRTRACK(g_CurrCdrom) != NULL)
  2061. {
  2062. for ( tr = CURRTRACK(g_CurrCdrom)->nextplay;
  2063. tr != NULL;
  2064. tr = tr->nextplay )
  2065. {
  2066. FigureTrackTime( g_CurrCdrom, tr->TocIndex, &mtemp, &stemp );
  2067. m+=mtemp;
  2068. s+=stemp;
  2069. }
  2070. m+= CDTIME(g_CurrCdrom).TrackRemMin;
  2071. s+= CDTIME(g_CurrCdrom).TrackRemSec;
  2072. }
  2073. m += (s / 60);
  2074. s = (s % 60);
  2075. CDTIME(g_CurrCdrom).RemMin = m;
  2076. CDTIME(g_CurrCdrom).RemSec = s;
  2077. //convert to a total number of seconds remaining
  2078. s = (m*60) + s;
  2079. //convert total time to a number of seconds
  2080. DWORD stotal = (CDTIME(g_CurrCdrom).TotalMin*60) + CDTIME(g_CurrCdrom).TotalSec;
  2081. //subtract time remaining from total time
  2082. stotal = stotal - s;
  2083. m = (stotal / 60);
  2084. s = (stotal % 60);
  2085. wsprintf( lpsz, DISC_TIME_FORMAT,
  2086. m,
  2087. g_szTimeSep,
  2088. s);
  2089. }
  2090. if (g_fDisplayDr) {
  2091. /*
  2092. ** Compute remaining time
  2093. */
  2094. mtemp = stemp = m = s =0;
  2095. if (CURRTRACK(g_CurrCdrom) != NULL) {
  2096. for ( tr = CURRTRACK(g_CurrCdrom)->nextplay;
  2097. tr != NULL;
  2098. tr = tr->nextplay ) {
  2099. FigureTrackTime( g_CurrCdrom, tr->TocIndex, &mtemp, &stemp );
  2100. m+=mtemp;
  2101. s+=stemp;
  2102. }
  2103. m+= CDTIME(g_CurrCdrom).TrackRemMin;
  2104. s+= CDTIME(g_CurrCdrom).TrackRemSec;
  2105. }
  2106. m+= (s / 60);
  2107. s = (s % 60);
  2108. CDTIME(g_CurrCdrom).RemMin = m;
  2109. CDTIME(g_CurrCdrom).RemSec = s;
  2110. wsprintf( lpsz, DISC_REM_FORMAT,
  2111. CDTIME(g_CurrCdrom).RemMin,
  2112. g_szTimeSep,
  2113. CDTIME(g_CurrCdrom).RemSec );
  2114. }
  2115. SetWindowText( g_hwndControls[INDEX(IDC_LED)], lpsz );
  2116. if (Flags != DISPLAY_UPD_LED)
  2117. {
  2118. InvalidateRect(g_hwndControls[INDEX(IDC_LED)],NULL,FALSE);
  2119. UpdateWindow(g_hwndControls[INDEX(IDC_LED)]);
  2120. }
  2121. if (g_fIsIconic) {
  2122. //Next four lines changed to fix tooltip bugs: <mwetzel 08.26.97>
  2123. if( g_Devices[g_CurrCdrom]->State & CD_PAUSED )
  2124. wsprintf( lpszIcon, IdStr( STR_CDPLAYER_PAUSED), lpsz );
  2125. else
  2126. wsprintf( lpszIcon, IdStr( STR_CDPLAYER_TIME ), lpsz );
  2127. SetWindowText( g_hwndApp, lpszIcon );
  2128. }
  2129. }
  2130. //update the framework window to show the time
  2131. if ((CURRTRACK(g_CurrCdrom)) && (lpsz[0] != TEXT('\0')))
  2132. {
  2133. //might already be pre-pending track number
  2134. if (lpsz[0] != TEXT('['))
  2135. {
  2136. wsprintf(lpszIcon,TEXT("[%i] %s"),CURRTRACK(g_CurrCdrom)->TocIndex+1,lpsz);
  2137. }
  2138. else
  2139. {
  2140. _tcscpy(lpszIcon,lpsz);
  2141. }
  2142. MMSETTITLE mmTitle;
  2143. mmTitle.mmInfoText = MMINFOTEXT_DESCRIPTION;
  2144. mmTitle.szTitle = lpszIcon;
  2145. g_pSink->OnEvent(MMEVENT_SETTITLE,&mmTitle);
  2146. }
  2147. /*
  2148. ** Update Title?
  2149. */
  2150. if (Flags & DISPLAY_UPD_TITLE_NAME)
  2151. {
  2152. ComputeDriveComboBox( );
  2153. SetWindowText( g_hwndControls[INDEX(IDC_TITLE_NAME)],
  2154. (LPCTSTR)TITLE(g_CurrCdrom) );
  2155. //update the framework window to show the title
  2156. MMSETTITLE mmTitle;
  2157. mmTitle.mmInfoText = MMINFOTEXT_TITLE;
  2158. mmTitle.szTitle = TITLE(g_CurrCdrom);
  2159. g_pSink->OnEvent(MMEVENT_SETTITLE,&mmTitle);
  2160. }
  2161. }
  2162. /******************************Public*Routine******************************\
  2163. * Common_OnCtlColor
  2164. *
  2165. * Here we return a brush to paint the background with. The brush is the same
  2166. * color as the face of a button. We also set the text background color so
  2167. * that static controls draw correctly. This function is shared with the
  2168. * disk info/editing dialog box.
  2169. *
  2170. * History:
  2171. * dd-mm-93 - StephenE - Created
  2172. *
  2173. \**************************************************************************/
  2174. HBRUSH
  2175. Common_OnCtlColor(
  2176. HWND hwnd,
  2177. HDC hdc,
  2178. HWND hwndChild,
  2179. int type
  2180. )
  2181. {
  2182. SetBkColor( hdc, GetSysColor(COLOR_BTNFACE) );
  2183. return g_hBrushBkgd;
  2184. }
  2185. /******************************Public*Routine******************************\
  2186. * Common_OnMeasureItem
  2187. *
  2188. * All items are the same height and width.
  2189. *
  2190. * We only have to update the height field for owner draw combo boxes and
  2191. * list boxes. This function is shared with the disk edit/info dialog box.
  2192. *
  2193. * History:
  2194. * dd-mm-93 - StephenE - Created
  2195. *
  2196. \**************************************************************************/
  2197. BOOL
  2198. Common_OnMeasureItem(
  2199. HWND hwnd,
  2200. MEASUREITEMSTRUCT *lpMeasureItem
  2201. )
  2202. {
  2203. if (lpMeasureItem->CtlType == ODT_MENU)
  2204. {
  2205. return FALSE;
  2206. }
  2207. HFONT hFont;
  2208. int cyBorder, cyDelta;
  2209. LOGFONT lf;
  2210. hFont = GetWindowFont( hwnd );
  2211. if ( hFont != NULL ) {
  2212. GetObject( hFont, sizeof(lf), &lf );
  2213. }
  2214. else {
  2215. SystemParametersInfo( SPI_GETICONTITLELOGFONT,
  2216. sizeof(lf), (LPVOID)&lf, 0 );
  2217. }
  2218. cyDelta = ABS( lf.lfHeight ) / 2;
  2219. cyBorder = GetSystemMetrics( SM_CYBORDER );
  2220. //
  2221. // Ensure enough room between chars.
  2222. //
  2223. if (cyDelta < 4 * cyBorder) {
  2224. cyDelta = 4 * cyBorder;
  2225. }
  2226. lpMeasureItem->itemHeight = ABS( lf.lfHeight ) + cyDelta;
  2227. return TRUE;
  2228. }
  2229. /******************************Public*Routine******************************\
  2230. * DrawTrackItem
  2231. *
  2232. * This routine draws the information in a cell of the track name
  2233. * combo box.
  2234. *
  2235. * History:
  2236. * 18-11-93 - StephenE - Created
  2237. *
  2238. \**************************************************************************/
  2239. void
  2240. DrawTrackItem(
  2241. HDC hdc,
  2242. const RECT *r,
  2243. DWORD item,
  2244. BOOL selected
  2245. )
  2246. {
  2247. SIZE si;
  2248. int i;
  2249. int cxTrk;
  2250. PTRACK_INF t;
  2251. TCHAR s[ARTIST_LENGTH];
  2252. TCHAR szTrk[16];
  2253. /*
  2254. ** Check for invalid items
  2255. */
  2256. if ( item == (DWORD)-1 ) {
  2257. return;
  2258. }
  2259. if ( ALLTRACKS(g_CurrCdrom) == NULL ) {
  2260. return;
  2261. }
  2262. /*
  2263. ** Check selection status, and set up to draw correctly
  2264. */
  2265. if ( selected ) {
  2266. SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
  2267. SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
  2268. }
  2269. else {
  2270. SetBkColor( hdc, GetSysColor(COLOR_WINDOW));
  2271. SetTextColor( hdc, GetSysColor(COLOR_WINDOWTEXT));
  2272. }
  2273. /*
  2274. ** Get track info
  2275. */
  2276. t = FindTrackNodeFromTocIndex( item, ALLTRACKS( g_CurrCdrom ) );
  2277. if ( (t != NULL) && (t->name != NULL) ) {
  2278. /*
  2279. ** Do we need to munge track name (clip to listbox)?
  2280. */
  2281. wsprintf(szTrk, TEXT("<%02d> "), t->TocIndex + FIRSTTRACK(g_CurrCdrom));
  2282. GetTextExtentPoint( hdc, szTrk, _tcslen(szTrk), &si );
  2283. cxTrk = si.cx;
  2284. i = _tcslen( t->name ) + 1;
  2285. do {
  2286. GetTextExtentPoint( hdc, t->name, --i, &si );
  2287. } while( si.cx > (r->right - r->left - cxTrk) );
  2288. ZeroMemory( s, TRACK_TITLE_LENGTH * sizeof( TCHAR ) );
  2289. _tcsncpy( s, t->name, i );
  2290. }
  2291. else {
  2292. _tcscpy( s, g_szBlank );
  2293. i = 1;
  2294. }
  2295. /*
  2296. ** Draw track name
  2297. */
  2298. ExtTextOut( hdc, r->left, r->top,
  2299. ETO_OPAQUE | ETO_CLIPPED,
  2300. r, s, i, NULL );
  2301. /*
  2302. ** draw track number
  2303. */
  2304. if ( t != NULL ) {
  2305. ExtTextOut( hdc, r->right - cxTrk, r->top, ETO_CLIPPED,
  2306. r, szTrk, _tcslen( szTrk ), NULL );
  2307. }
  2308. }
  2309. /*****************************Private*Routine******************************\
  2310. * HandlePassedCommandLine
  2311. *
  2312. * This function gets called to handle command line options that are passed to
  2313. * CDPlayer as the result of the WM_DROPFILES or WM_COPYDATA messages.
  2314. *
  2315. * History:
  2316. * dd-mm-94 - StephenE - Created
  2317. *
  2318. \**************************************************************************/
  2319. void
  2320. HandlePassedCommandLine(
  2321. LPTSTR lpCmdLine,
  2322. BOOL fCheckCDRom
  2323. )
  2324. {
  2325. int i;
  2326. int iTrack = -1, iCDrom;
  2327. iCDrom = ParseCommandLine( lpCmdLine, &iTrack, TRUE );
  2328. if ((iCDrom < 0) || (iCDrom >= g_NumCdDevices))
  2329. return;
  2330. // CheckUnitCDRom to reload Table of Contents
  2331. if ( fCheckCDRom )
  2332. {
  2333. CheckUnitCdrom(iCDrom, TRUE);
  2334. while( !LockTableOfContents(iCDrom) )
  2335. {
  2336. MSG msg;
  2337. GetMessage( &msg, NULL, WM_NOTIFY_TOC_READ, WM_NOTIFY_TOC_READ );
  2338. DispatchMessage( &msg );
  2339. }
  2340. }
  2341. #if 0
  2342. if (iCDrom != g_CurrCdrom)
  2343. {
  2344. HWND hwndBtn = g_hwndControls[INDEX(IDC_ARTIST_NAME)];
  2345. SwitchToCdrom( iCDrom, TRUE );
  2346. SetPlayButtonsEnableState();
  2347. SendMessage( hwndBtn, CB_SETCURSEL, (WPARAM)iCDrom, 0 );
  2348. }
  2349. #endif
  2350. /*
  2351. ** Initialize the new play list for each drive.
  2352. */
  2353. for ( i = 0; i < g_NumCdDevices; i++)
  2354. {
  2355. TimeAdjustInitialize( i );
  2356. }
  2357. // Set Current Track to specified track
  2358. if ( iTrack != -1 )
  2359. {
  2360. PTRACK_PLAY tr;
  2361. tr = PLAYLIST( iCDrom );
  2362. if ( tr != NULL )
  2363. {
  2364. for( i = 0; i < iTrack; i++, tr = tr->nextplay );
  2365. TimeAdjustSkipToTrack( iCDrom, tr );
  2366. }
  2367. }
  2368. }
  2369. /******************************Public*Routine******************************\
  2370. * IsUpdateOptionGiven
  2371. *
  2372. * Checks the command line to see if the "-update" option has been passed.
  2373. *
  2374. * History:
  2375. * dd-mm-95 - StephenE - Created
  2376. *
  2377. \**************************************************************************/
  2378. BOOL
  2379. IsUpdateOptionGiven(
  2380. LPTSTR lpstr
  2381. )
  2382. {
  2383. TCHAR chOption[MAX_PATH];
  2384. /*
  2385. ** Start by converting everything to upper case.
  2386. */
  2387. CharUpperBuff( lpstr, _tcslen(lpstr) );
  2388. /*
  2389. ** The first parameter on the command line is always the
  2390. ** string used invoke the program. ie cdplayer.exe
  2391. */
  2392. lpstr += _tcsspn( lpstr, g_szBlank );
  2393. lpstr += CopyWord( chOption, lpstr );
  2394. /*
  2395. ** Remove leading space
  2396. */
  2397. lpstr += _tcsspn( lpstr, g_szBlank );
  2398. /*
  2399. ** process any command line options
  2400. */
  2401. while ( (*lpstr == g_chOptionHyphen) || (*lpstr == g_chOptionSlash) ) {
  2402. /*
  2403. ** pass over the option delimeter
  2404. */
  2405. lpstr++;
  2406. /*
  2407. ** Copy option and skip over it.
  2408. */
  2409. lpstr += CopyWord( chOption, lpstr );
  2410. /*
  2411. ** Is this the play option ?? If so, don't bother processing anymore
  2412. ** options.
  2413. */
  2414. if ( 0 == _tcscmp( chOption, g_szUpdate ) ) {
  2415. return TRUE;
  2416. }
  2417. /*
  2418. ** Remove leading space
  2419. */
  2420. lpstr += _tcsspn( lpstr, g_szBlank );
  2421. }
  2422. return FALSE;
  2423. }
  2424. /*****************************Private*Routine******************************\
  2425. * ParseCommandLine
  2426. *
  2427. * Here we look to see if we have been asked to play a particular track.
  2428. * The format of the command line is:
  2429. *
  2430. *
  2431. * CDPlayer command options.
  2432. *
  2433. * CDPLAYER {Options}
  2434. *
  2435. * Options : -play | {Sub-Options}
  2436. * Sub-Options : {-track tracklist} | trackname
  2437. * trackname : filename | drive letter
  2438. * tracklist : filename+
  2439. *
  2440. * -track A track list if a list of tracks that the user wants
  2441. * to play. It overides any play list that may already be stored
  2442. * for the current cd.
  2443. *
  2444. * -play Start playing the current play list. If -play is not specified
  2445. * CD Player seeks to the first track in the play list.
  2446. *
  2447. *
  2448. * On Windows NT the format of [file] is:
  2449. * d:\track(nn).cda
  2450. *
  2451. * where d: is the drive letter of the cdrom that you want to play
  2452. * and \track(nn) is the track number that you want to play.
  2453. *
  2454. * Therefore to play track 8 from a cd-rom drive mapped to e:
  2455. *
  2456. * cdplayer /play e:\track08.cda
  2457. *
  2458. * On Chicago the file is actually a riff CDDA file which contains
  2459. * all the info required to locate the disc and track.
  2460. *
  2461. * Returns the index of the CD-Rom drive which should be played first. Can return
  2462. * -1 iff the caller passed FALSE for the fQuiet parameter and the message box was
  2463. * actually displayed. This should only occur when the user trys to start a new
  2464. * copy of cdplayer passing it the track.cda file of a disk that is not inserted
  2465. * in any of the current CD-Drives attached to the machine.
  2466. *
  2467. * History:
  2468. * dd-mm-94 - StephenE - Created
  2469. *
  2470. \**************************************************************************/
  2471. int
  2472. ParseCommandLine(
  2473. LPTSTR lpstr,
  2474. int *piTrackToSeekTo,
  2475. BOOL fQuiet
  2476. )
  2477. {
  2478. TCHAR chOption[MAX_PATH];
  2479. BOOL fTrack = FALSE;
  2480. int iCdRomIndex = -1; // Assume Failure until proven otherwise
  2481. int ii;
  2482. for (ii = 0; ii < g_NumCdDevices; ii++) {
  2483. g_Devices[ii]->fKilledPlayList = FALSE;
  2484. }
  2485. /*
  2486. ** Start by converting everything to upper case.
  2487. */
  2488. CharUpperBuff( lpstr, _tcslen(lpstr) );
  2489. #if DBG
  2490. #ifdef UNICODE
  2491. dprintf( TEXT("CD Player Command line : %ls"), lpstr );
  2492. #else
  2493. dprintf( "CD Player Command line : %s", lpstr );
  2494. #endif
  2495. #endif
  2496. /*
  2497. ** The first parameter on the command line is always the
  2498. ** string used invoke the program. ie cdplayer.exe
  2499. */
  2500. lpstr += _tcsspn( lpstr, g_szBlank );
  2501. lpstr += CopyWord( chOption, lpstr );
  2502. /*
  2503. ** Remove leading space
  2504. */
  2505. lpstr += _tcsspn( lpstr, g_szBlank );
  2506. /*
  2507. ** process any command line options
  2508. */
  2509. while ( (*lpstr == g_chOptionHyphen) || (*lpstr == g_chOptionSlash) ) {
  2510. /*
  2511. ** pass over the option delimeter
  2512. */
  2513. lpstr++;
  2514. /*
  2515. ** Copy option and skip over it.
  2516. */
  2517. lpstr += CopyWord( chOption, lpstr );
  2518. /*
  2519. ** Is this a command line option we understand - ignore ones
  2520. ** we don't understand.
  2521. */
  2522. if ( 0 == _tcscmp( chOption, g_szTrack ) )
  2523. {
  2524. if ( !fTrack )
  2525. {
  2526. lpstr = ParseTrackList( lpstr, &iCdRomIndex );
  2527. fTrack = TRUE;
  2528. }
  2529. }
  2530. else if ( 0 == _tcscmp( chOption, g_szPlay ) )
  2531. {
  2532. g_fPlay = TRUE;
  2533. }
  2534. else if ( 0 == _tcscmp( chOption, g_szTray) )
  2535. {
  2536. g_fStartedInTray = TRUE;
  2537. }
  2538. else
  2539. {
  2540. #if DBG
  2541. #ifdef UNICODE
  2542. dprintf(TEXT("Ignoring unknown option %ls\n"), chOption );
  2543. #else
  2544. dprintf("Ignoring unknown option %s\n", chOption );
  2545. #endif
  2546. #endif
  2547. }
  2548. /*
  2549. ** Remove leading space
  2550. */
  2551. lpstr += _tcsspn( lpstr, g_szBlank );
  2552. }
  2553. /*
  2554. ** parse remaining command line parameters
  2555. */
  2556. if ( (*lpstr != g_chNULL) && !fTrack) {
  2557. /*
  2558. ** Copy track name and skip over it. Sometimes the shell
  2559. ** gives us quoted strings and sometimes it doesn't. If the
  2560. ** string is not quoted assume that remainder of the command line
  2561. ** is the track name.
  2562. */
  2563. if ( (*lpstr == TEXT('\'')) || (*lpstr == TEXT('\"')) ) {
  2564. lpstr += CopyWord( chOption, lpstr );
  2565. }
  2566. else {
  2567. _tcscpy(chOption, lpstr);
  2568. }
  2569. if ( IsTrackFileNameValid( chOption, &iCdRomIndex,
  2570. piTrackToSeekTo, FALSE, fQuiet ) )
  2571. {
  2572. //if the user passed in a track, turn off start-up random mode
  2573. if (!g_fSelectedOrder)
  2574. {
  2575. g_fSelectedOrder = TRUE;
  2576. SendMessage(GetParent(g_hwndApp),WM_COMMAND,MAKEWPARAM(IDM_MODE_NORMAL,0),(LPARAM)0);
  2577. }
  2578. ResetPlayList( iCdRomIndex );
  2579. }
  2580. #if DBG
  2581. #ifdef UNICODE
  2582. dprintf(TEXT("Seeking to track %ls\n"), chOption );
  2583. #else
  2584. dprintf("Seeking to track %s\n", chOption );
  2585. #endif
  2586. #endif
  2587. }
  2588. return iCdRomIndex;
  2589. }
  2590. /*****************************Private*Routine******************************\
  2591. * ParseTrackList
  2592. *
  2593. * Each track is separated by a ' ' character. The track list is terminated
  2594. * by the NULL, '/' or '-' character.
  2595. *
  2596. * History:
  2597. * dd-mm-94 - StephenE - Created
  2598. *
  2599. \**************************************************************************/
  2600. TCHAR *
  2601. ParseTrackList(
  2602. TCHAR *szTrackList,
  2603. int *piCdRomIndex
  2604. )
  2605. {
  2606. TCHAR chTrack[MAX_PATH];
  2607. int iTrackIndex;
  2608. int iCdRom = -1; // Assume failure, until proven otherwise
  2609. BOOL fPlayListErased = FALSE;
  2610. ZeroMemory (&chTrack, sizeof (chTrack)); // Make Prefix happy.
  2611. /*
  2612. ** Remove any stray white space
  2613. */
  2614. szTrackList += _tcsspn( szTrackList, g_szBlank );
  2615. /*
  2616. ** While there are still valid characters to process
  2617. */
  2618. while ( (*szTrackList != g_chNULL)
  2619. && (*szTrackList != g_chOptionHyphen)
  2620. && (*szTrackList != g_chOptionSlash) ) {
  2621. /*
  2622. ** Copy the track name and skip over it.
  2623. */
  2624. szTrackList += CopyWord( chTrack, szTrackList );
  2625. /*
  2626. ** Now check that we have been given a valid filename
  2627. */
  2628. if ( IsTrackFileNameValid( chTrack, &iCdRom, &iTrackIndex, TRUE, FALSE ) ) {
  2629. PTRACK_PLAY pt;
  2630. /*
  2631. ** If this is the first valid file given nuke the
  2632. ** existing play lists and prepare for a new list. Note that
  2633. ** things are complicated by the fact that we could be given
  2634. ** files from more than one CD-Rom drive.
  2635. */
  2636. if (! g_Devices[iCdRom]->fKilledPlayList)
  2637. {
  2638. /*
  2639. ** Kill the old play and save lists.
  2640. */
  2641. ErasePlayList( iCdRom );
  2642. EraseSaveList( iCdRom );
  2643. PLAYLIST( iCdRom ) = NULL;
  2644. SAVELIST( iCdRom ) = NULL;
  2645. fPlayListErased = TRUE;
  2646. g_Devices[iCdRom]->fKilledPlayList = TRUE;
  2647. *piCdRomIndex = iCdRom;
  2648. }
  2649. pt = (TRACK_PLAY*)AllocMemory( sizeof(TRACK_PLAY) );
  2650. pt->TocIndex = iTrackIndex;
  2651. pt->min = 0;
  2652. pt->sec = 0;
  2653. /*
  2654. ** Is this the first track on this devices play list ?
  2655. */
  2656. if ( PLAYLIST(iCdRom) == NULL ) {
  2657. PLAYLIST(iCdRom) = pt;
  2658. pt->nextplay = pt->prevplay = NULL;
  2659. }
  2660. else {
  2661. /*
  2662. ** append this track to the end of the current play list
  2663. */
  2664. AppendTrackToPlayList( PLAYLIST(iCdRom), pt );
  2665. }
  2666. }
  2667. else {
  2668. /*
  2669. ** Put up a message box warning the user that the given
  2670. ** track name is invalid and that we can't play it.
  2671. */
  2672. ;
  2673. }
  2674. /*
  2675. ** Remove any stray white space
  2676. */
  2677. szTrackList += _tcsspn( szTrackList, g_szBlank );
  2678. }
  2679. /*
  2680. ** If we have erased the play list we have to go off and reset the
  2681. ** saved play list.
  2682. */
  2683. if ( fPlayListErased ) {
  2684. SAVELIST( iCdRom ) = CopyPlayList( PLAYLIST(iCdRom) );
  2685. }
  2686. return szTrackList;
  2687. }
  2688. /*****************************Private*Routine******************************\
  2689. * CopyWord
  2690. *
  2691. * Copies one from szSource to szWord - assumes that words are delimited
  2692. * by ' ' characters. szSource MUST point to the begining of the word.
  2693. *
  2694. * Returns length of word copied.
  2695. *
  2696. * History:
  2697. * dd-mm-94 - StephenE - Created
  2698. *
  2699. \**************************************************************************/
  2700. int
  2701. CopyWord(
  2702. TCHAR *szWord,
  2703. TCHAR *szSource
  2704. )
  2705. {
  2706. int n, nReturn;
  2707. /*
  2708. ** Copy the track name
  2709. */
  2710. if ( (*szSource == TEXT('\'')) || (*szSource == TEXT('\"')) ) {
  2711. TCHAR ch = *szSource;
  2712. /*
  2713. ** Remember which quote character it was
  2714. ** According to the DOCS " is invalid in a filename...
  2715. */
  2716. n = 0;
  2717. /*
  2718. ** Move over the initial quote, then copy the filename
  2719. */
  2720. while ( *++szSource && *szSource != ch ) {
  2721. szWord[n++] = *szSource;
  2722. }
  2723. nReturn = n + (*szSource == ch ? 2 : 1);
  2724. }
  2725. else {
  2726. n = _tcscspn( szSource, g_szBlank );
  2727. _tcsncpy( szWord, szSource, n );
  2728. nReturn = n;
  2729. }
  2730. szWord[n] = g_chNULL;
  2731. return nReturn;
  2732. }
  2733. /*****************************Private*Routine******************************\
  2734. * IsTrackFileNameValid
  2735. *
  2736. * This function returns true if the specified filename is a valid CD track.
  2737. * On NT track filenames must be of the form:
  2738. * d:\track(n).cda where d: is the CD-Rom device and \track(n).cda
  2739. * is the index of the track to be played (starting from 1).
  2740. *
  2741. * On Chicago the track filename is actually a riff CDDA file which contains
  2742. * the track info that we require.
  2743. *
  2744. * If the filename is valid the function true and sets piCdromIndex and
  2745. * piTrackIndex to the correct values.
  2746. *
  2747. * History:
  2748. * 29-09-94 - StephenE - Created
  2749. *
  2750. \**************************************************************************/
  2751. BOOL
  2752. IsTrackFileNameValid(
  2753. LPTSTR lpstFileName,
  2754. int *piCdRomIndex,
  2755. int *piTrackIndex,
  2756. BOOL fScanningTracks,
  2757. BOOL fQuiet
  2758. )
  2759. {
  2760. #define RIFF_RIFF 0x46464952
  2761. #define RIFF_CDDA 0x41444443
  2762. RIFFCDA cda;
  2763. HANDLE hFile;
  2764. TCHAR chDriveLetter;
  2765. int i;
  2766. TCHAR szFileName[MAX_PATH];
  2767. TCHAR szPath[MAX_PATH];
  2768. SHFILEINFO shInfo;
  2769. DWORD cbRead;
  2770. //
  2771. // If we are not constructing a track play list it is valid to just specify
  2772. // a drive letter, in which case we select that drive and start playing
  2773. // at the first track on it. All the tracks are played in sequential
  2774. // order.
  2775. //
  2776. if ( !fScanningTracks) {
  2777. //
  2778. // Map the drive letter onto the internal CD-Rom index used by CDPlayer.
  2779. //
  2780. chDriveLetter = *lpstFileName;
  2781. for ( i = 0; i < g_NumCdDevices; i++ ) {
  2782. if (g_Devices[i]->drive == chDriveLetter) {
  2783. *piCdRomIndex = i;
  2784. break;
  2785. }
  2786. }
  2787. //
  2788. // If we mapped the drive OK check to see if we should play all
  2789. // the tracks or just the current play list for that drive. If we
  2790. // didn't map the drive OK assume that its the first part of a
  2791. // RIFF filename and fall through to the code below that opens the
  2792. // RIFF file and parses its contents.
  2793. //
  2794. if ( i != g_NumCdDevices ) {
  2795. //
  2796. // If next character is only a colon ':' then play the
  2797. // the entire disk starting from the first track.
  2798. //
  2799. if ( 0 == _tcscmp(lpstFileName + 1, g_szColon) ) {
  2800. *piTrackIndex = 0;
  2801. return TRUE;
  2802. }
  2803. //
  2804. // If the next two characters are colon backslash ":\" then
  2805. // we seek to the specified drive and play only those tracks that
  2806. // are in the default playlist for the current disk in that drive.
  2807. // All we need to do to achive this is return FALSE.
  2808. //
  2809. if ( 0 == _tcscmp(lpstFileName + 1, g_szColonBackSlash) ) {
  2810. return FALSE;
  2811. }
  2812. }
  2813. }
  2814. //
  2815. // Otherwise, open the file and read the CDA info. The file name may be a
  2816. // link to .cda in which case we need to get the shell to resolve the link for
  2817. // us. We take a copy of the file name because the ResolveLink function
  2818. // modifies the file name string in place.
  2819. //
  2820. _tcscpy(szFileName, lpstFileName);
  2821. if (0L == SHGetFileInfo( szFileName, 0L, &shInfo,
  2822. sizeof(shInfo), SHGFI_ATTRIBUTES)) {
  2823. return FALSE;
  2824. }
  2825. if ((shInfo.dwAttributes & SFGAO_LINK) == SFGAO_LINK) {
  2826. if (!g_fOleInitialized)
  2827. {
  2828. g_fOleInitialized = SUCCEEDED(OleInitialize(NULL));
  2829. }
  2830. if (!ResolveLink(szFileName)) {
  2831. return FALSE;
  2832. }
  2833. }
  2834. // Make sure file exists
  2835. if (GetFileAttributes (szFileName) == ((DWORD)-1)) {
  2836. // Get Full path to file
  2837. if (0 == SearchPath (NULL, szFileName, NULL,
  2838. MAX_PATH, szPath, NULL)) {
  2839. return FALSE;
  2840. }
  2841. } else {
  2842. _tcscpy (szPath, szFileName);
  2843. }
  2844. // Open file and read in CDA info
  2845. hFile = CreateFile (szFileName, GENERIC_READ,
  2846. FILE_SHARE_READ, NULL,
  2847. OPEN_EXISTING, 0, NULL);
  2848. if (INVALID_HANDLE_VALUE == hFile) {
  2849. return FALSE;
  2850. }
  2851. ReadFile(hFile, &cda, sizeof(cda), &cbRead, NULL);
  2852. CloseHandle (hFile);
  2853. //
  2854. // Make sure its a RIFF CDDA file
  2855. //
  2856. if ( (cda.dwRIFF != RIFF_RIFF) || (cda.dwCDDA != RIFF_CDDA) ) {
  2857. return FALSE;
  2858. }
  2859. //
  2860. // Make sure that we have this disc loaded.
  2861. //
  2862. for ( i = 0; i < g_NumCdDevices; i++ ) {
  2863. if (g_Devices[i]->CdInfo.Id == cda.DiscID) {
  2864. *piCdRomIndex = i;
  2865. break;
  2866. }
  2867. }
  2868. //
  2869. // If we didn't map the drive OK return FALSE AND set the
  2870. // returned CD-ROM index to -1 but only if the caller asked us
  2871. // to complain about an incorrect CD being inserted in the drive.
  2872. //
  2873. if ( i == g_NumCdDevices ) {
  2874. if (!fQuiet) {
  2875. AskUserToInsertCorrectDisc(cda.DiscID);
  2876. *piCdRomIndex = -1;
  2877. }
  2878. return FALSE;
  2879. }
  2880. *piTrackIndex = cda.wTrack - 1;
  2881. return TRUE;
  2882. }
  2883. /*****************************Private*Routine******************************\
  2884. * AppendTrackToPlayList
  2885. *
  2886. * Appends the TRACK_PLAY record pointed to by pAppend to the end of the
  2887. * double linked list pointed to by pHead.
  2888. *
  2889. *
  2890. * History:
  2891. * dd-mm-94 - StephenE - Created
  2892. *
  2893. \**************************************************************************/
  2894. void
  2895. AppendTrackToPlayList(
  2896. PTRACK_PLAY pHead,
  2897. PTRACK_PLAY pAppend
  2898. )
  2899. {
  2900. PTRACK_PLAY pp = pHead;
  2901. while (pp->nextplay != NULL) {
  2902. pp = pp->nextplay;
  2903. }
  2904. pp->nextplay = pAppend;
  2905. pAppend->prevplay = pp;
  2906. pAppend->nextplay = NULL;
  2907. }
  2908. /*****************************Private*Routine******************************\
  2909. * FindMostSuitableDrive
  2910. *
  2911. * Tries to determine the best drive to make the current drive. Returns the
  2912. * drive.
  2913. *
  2914. * We should choose the first disc that is playing if any are playing.
  2915. *
  2916. * New dstewart: Else choose the drive that is selected in the CDUNIT table
  2917. *
  2918. * Else we should choose the first disc with a music disk in it if there
  2919. * any drives with music discs in them.
  2920. *
  2921. * Else we should chose the first drive that is available if any of the
  2922. * drives are available.
  2923. *
  2924. * Else just choose the first (ie. zeroth) drive.
  2925. *
  2926. * History:
  2927. * dd-mm-94 - StephenE - Created
  2928. *
  2929. \**************************************************************************/
  2930. int
  2931. FindMostSuitableDrive(
  2932. void
  2933. )
  2934. {
  2935. int iDisc;
  2936. /*
  2937. ** Check for a playing drive
  2938. */
  2939. for ( iDisc = 0; iDisc < g_NumCdDevices; iDisc++ ) {
  2940. if ( g_Devices[iDisc]->State & (CD_PLAYING | CD_PAUSED) ) {
  2941. return iDisc;
  2942. }
  2943. }
  2944. //check the current default drive
  2945. LPCDOPT pOpt = (LPCDOPT)g_pSink->GetOptions();
  2946. LPCDOPTIONS pCDOpts = NULL;
  2947. LPCDUNIT pUnit = NULL;
  2948. int iDefDrive = 0;
  2949. if (pOpt)
  2950. {
  2951. pCDOpts = pOpt->GetCDOpts();
  2952. }
  2953. if (pCDOpts)
  2954. {
  2955. pUnit = pCDOpts->pCDUnitList;
  2956. }
  2957. //scan the list to find the one we want
  2958. for (int index = 0; index < g_NumCdDevices; index++)
  2959. {
  2960. if (pUnit)
  2961. {
  2962. if (pUnit->fDefaultDrive)
  2963. {
  2964. iDefDrive = index;
  2965. //if this is the default AND it has a disc loaded, go for it
  2966. if ( g_Devices[index]->State & CD_LOADED )
  2967. {
  2968. return index;
  2969. }
  2970. }
  2971. pUnit = pUnit->pNext;
  2972. }
  2973. }
  2974. /*
  2975. ** Check for a drive with a music disk in it
  2976. */
  2977. for ( iDisc = 0; iDisc < g_NumCdDevices; iDisc++ )
  2978. {
  2979. if ( g_Devices[iDisc]->State & CD_LOADED )
  2980. {
  2981. return iDisc;
  2982. }
  2983. }
  2984. /*
  2985. ** If the default drive is not in use, use it
  2986. */
  2987. if ( (g_Devices[iDefDrive]->State & (CD_BEING_SCANNED | CD_IN_USE)) == 0 )
  2988. {
  2989. return iDefDrive;
  2990. }
  2991. /*
  2992. ** Check for any drive that is not in use
  2993. */
  2994. for ( iDisc = 0; iDisc < g_NumCdDevices; iDisc++ )
  2995. {
  2996. if ( (g_Devices[iDisc]->State & (CD_BEING_SCANNED | CD_IN_USE)) == 0 )
  2997. {
  2998. return iDisc;
  2999. }
  3000. }
  3001. /*
  3002. ** Ok, no disc are loaded, but all disc are in use, just use the default
  3003. */
  3004. return iDefDrive;
  3005. }
  3006. /*****************************Private*Routine******************************\
  3007. * AskUserToInsertCorrectDisc
  3008. *
  3009. *
  3010. *
  3011. * History:
  3012. * dd-mm-94 - StephenE - Created
  3013. *
  3014. \**************************************************************************/
  3015. void
  3016. AskUserToInsertCorrectDisc(
  3017. DWORD dwID
  3018. )
  3019. {
  3020. TCHAR szMsgBoxTitle[32];
  3021. TCHAR szDiskTitle[TITLE_LENGTH];
  3022. TCHAR szArtistName[ARTIST_LENGTH];
  3023. TCHAR szFormat[STR_MAX_STRING_LEN];
  3024. TCHAR szText[STR_MAX_STRING_LEN + TITLE_LENGTH];
  3025. LPCDDATA pData = (LPCDDATA)g_pSink->GetData();
  3026. _tcscpy(szDiskTitle,g_szNothingThere);
  3027. if(pData)
  3028. {
  3029. //
  3030. // Try to read in title from the options database
  3031. //
  3032. if (pData->QueryTitle(dwID))
  3033. {
  3034. //
  3035. // We found an entry for this disc, so copy all the information
  3036. // from the title database
  3037. LPCDTITLE pCDTitle = NULL;
  3038. if (pData->LockTitle(&pCDTitle,dwID))
  3039. {
  3040. _tcscpy(szDiskTitle,pCDTitle->szTitle);
  3041. _tcscpy(szArtistName,pCDTitle->szArtist);
  3042. pData->UnlockTitle(pCDTitle,FALSE);
  3043. } //end if title locked
  3044. } //end if title found
  3045. }
  3046. /*
  3047. ** If the disk title was found in the database display it.
  3048. */
  3049. if (_tcscmp(szDiskTitle, g_szNothingThere) != 0)
  3050. {
  3051. _tcscpy( szFormat, IdStr(STR_DISK_NOT_THERE_K) );
  3052. wsprintf(szText, szFormat, szDiskTitle, szArtistName);
  3053. }
  3054. else
  3055. {
  3056. _tcscpy( szText, IdStr(STR_DISK_NOT_THERE) );
  3057. }
  3058. //
  3059. // If CD Player is minimized make sure it is restored
  3060. // before displaying the MessageBox
  3061. //
  3062. if (IsIconic(g_hwndApp)) {
  3063. WINDOWPLACEMENT wndpl;
  3064. wndpl.length = sizeof(WINDOWPLACEMENT);
  3065. GetWindowPlacement(g_hwndApp, &wndpl);
  3066. wndpl.showCmd = SW_RESTORE;
  3067. SetWindowPlacement(g_hwndApp, &wndpl);
  3068. }
  3069. _tcscpy( szMsgBoxTitle, IdStr(STR_CDPLAYER) );
  3070. MessageBox( g_hwndApp, szText, szMsgBoxTitle,
  3071. MB_SETFOREGROUND | MB_ICONINFORMATION | MB_APPLMODAL | MB_OK);
  3072. }
  3073. #ifndef USE_IOCTLS
  3074. BOOL CheckMCICDA (TCHAR chDrive)
  3075. {
  3076. DWORD cchLen;
  3077. DWORD dwResult;
  3078. DWORD dwErr;
  3079. CDHANDLE hCD;
  3080. TCHAR szPath[MAX_PATH];
  3081. TCHAR szText[512];
  3082. TCHAR szTitle[MAX_PATH];
  3083. // Make sure the mcicda.dll exists
  3084. cchLen = NUMELEMS(szPath);
  3085. dwResult = SearchPath (NULL, TEXT ("mcicda.dll"), NULL,
  3086. cchLen, szPath, NULL);
  3087. if ((! dwResult) ||
  3088. (0xFFFFFFFF == GetFileAttributes (szPath)))
  3089. {
  3090. // Give Missing MCICDA.DLL error message
  3091. GetSystemDirectory (szPath, cchLen);
  3092. _tcscpy( szTitle, IdStr( STR_MCICDA_MISSING ) );
  3093. wsprintf (szText, szTitle, szPath);
  3094. _tcscpy( szTitle, IdStr( STR_CDPLAYER ) );
  3095. MessageBox( NULL, szText, szTitle,
  3096. MB_APPLMODAL | MB_ICONINFORMATION |
  3097. MB_OK | MB_SETFOREGROUND );
  3098. return FALSE;
  3099. }
  3100. // Make sure mcicda.dll service is up and running
  3101. hCD = OpenCdRom (chDrive, &dwErr);
  3102. if (! hCD)
  3103. {
  3104. DWORD SessionId = 0;
  3105. ProcessIdToSessionId( GetCurrentProcessId(), &SessionId );
  3106. // Error loading media device driver.
  3107. if (SessionId != 0){ //Remote connection user
  3108. _tcscpy( szText, IdStr( STR_MCICDA_NOT_AVAIL ) );
  3109. }
  3110. else {
  3111. _tcscpy( szText, IdStr( STR_MCICDA_NOT_WORKING ) );
  3112. }
  3113. _tcscpy( szTitle, IdStr( STR_CDPLAYER ) );
  3114. MessageBox( NULL, szText, szTitle,
  3115. MB_APPLMODAL | MB_ICONINFORMATION |
  3116. MB_OK | MB_SETFOREGROUND );
  3117. return FALSE;
  3118. }
  3119. // Close Device
  3120. CloseCdRom (hCD);
  3121. return TRUE;
  3122. }
  3123. #endif // ! USE_IOCTLS
  3124. #if DBG
  3125. /******************************Public*Routine******************************\
  3126. * CDAssert
  3127. *
  3128. *
  3129. * History:
  3130. * 18-11-93 - StephenE - Created
  3131. *
  3132. \**************************************************************************/
  3133. void
  3134. CDAssert(
  3135. LPSTR x,
  3136. LPSTR file,
  3137. int line
  3138. )
  3139. {
  3140. TCHAR buff[128];
  3141. wsprintf( buff, TEXT("%s \nat line %d of %s"), x, line, file );
  3142. MessageBox( NULL, buff, TEXT("Assertion Failure:"), MB_APPLMODAL | MB_OK );
  3143. }
  3144. /******************************Public*Routine******************************\
  3145. * dprintf
  3146. *
  3147. *
  3148. *
  3149. * History:
  3150. * dd-mm-94 - StephenE - Created
  3151. *
  3152. \**************************************************************************/
  3153. void
  3154. dprintf(
  3155. TCHAR *lpszFormat,
  3156. ...
  3157. )
  3158. {
  3159. TCHAR buf[512];
  3160. UINT n;
  3161. va_list va;
  3162. static int iPrintOutput = -1;
  3163. if (iPrintOutput == -1) {
  3164. iPrintOutput = GetProfileInt( TEXT("MMDEBUG"), TEXT("CdPlayer"), 0);
  3165. }
  3166. if (iPrintOutput) {
  3167. n = wsprintf(buf, TEXT("CdPlayer: <%d>"), GetCurrentThreadId() );
  3168. va_start(va, lpszFormat);
  3169. n += wvsprintf(buf+n, lpszFormat, va);
  3170. va_end(va);
  3171. buf[n++] = '\n';
  3172. buf[n] = 0;
  3173. OutputDebugString(buf);
  3174. }
  3175. }
  3176. #endif // End #ifdef DBG
  3177. /******************************Public*Routine******************************\
  3178. * ChildEnumProc
  3179. *
  3180. * Gets the position of each child control window. As saves the associated
  3181. * window handle for later use.
  3182. *
  3183. * History:
  3184. * 18-11-93 - StephenE - Created
  3185. *
  3186. \**************************************************************************/
  3187. BOOL CALLBACK
  3188. ChildEnumProc(
  3189. HWND hwndChild,
  3190. LPARAM hwndParent
  3191. )
  3192. {
  3193. int index = 0;
  3194. index = INDEX(GetDlgCtrlID( hwndChild ));
  3195. if ((index > -1) && (index < NUM_OF_CONTROLS))
  3196. {
  3197. g_hwndControls[index] = hwndChild;
  3198. }
  3199. return TRUE;
  3200. }
  3201.