/******************************Module*Header*******************************\ * Module Name: cdplayer.c * * CD Playing application * * * Created: 02-11-93 * Author: Stephen Estrop [StephenE] * * Copyright (c) 1993 - 1995 Microsoft Corporation. All rights reserved. \**************************************************************************/ #pragma warning( once : 4201 4214 ) #include /* required for all Windows applications */ #include #include #include #include #include #define NOMENUHELP #define NOBTNLIST #define NOTRACKBAR #define NODRAGLIST #define NOUPDOWN #include /* want toolbar and status bar */ #include #include #include /* contains portable ascii/unicode macros */ #include #include #define GLOBAL /* This allocates storage for the public globals */ #include "resource.h" #include "cdplayer.h" #include "ledwnd.h" #include "cdapi.h" #include "scan.h" #include "trklst.h" #include "database.h" #include "commands.h" #include "buttons.h" #include "preferen.h" #include "literals.h" //#ifndef WM_CDPLAYER_COPYDATA #define WM_CDPLAYER_COPYDATA (WM_USER+0x100) //#endif /* ------------------------------------------------------------------------- ** Private functions ** ------------------------------------------------------------------------- */ void StartSndVol( DWORD unused ); int CopyWord( TCHAR *szWord, TCHAR *szSource ); void AppendTrackToPlayList( PTRACK_PLAY pHead, PTRACK_PLAY pInsert ); BOOL IsTrackFileNameValid( LPTSTR lpstFileName, int *piCdRomIndex, int *piTrackIndex, BOOL fScanningTracks, BOOL fQuiet ); TCHAR * ParseTrackList( TCHAR *szTrackList, int *piCdRomIndex ); int ParseCommandLine( LPTSTR lpstr, int *piTrackToSeekTo, BOOL fQuiet ); void HandlePassedCommandLine( LPTSTR lpCmdLine, BOOL fCheckCDRom ); int FindMostSuitableDrive( void ); void AskUserToInsertCorrectDisc( DWORD dwID ); #ifndef USE_IOCTLS BOOL CheckMCICDA (TCHAR chDrive); #endif // ! USE_IOCTLS BOOL CDPlay_CopyData( HWND hwnd, PCOPYDATASTRUCT lpcpds ); /* ------------------------------------------------------------------------- ** Private Globals ** ------------------------------------------------------------------------- */ RECT rcToolbar; RECT rcStatusbar; RECT rcTrackInfo; RECT rcControls[NUM_OF_CONTROLS]; long cyTrackInfo; int cyMenuCaption; HICON hIconCdPlayer; HBRUSH g_hBrushBkgd; BOOL g_fTitlebarShowing = TRUE; BOOL g_fVolumeController; BOOL g_fTrackInfoVisible = 1; TCHAR g_szTimeSep[10]; int g_AcceleratorCount; BOOL g_fInCopyData = FALSE; CRITICAL_SECTION g_csTOCSerialize; //--------------------------------------------------------------------------- // Stuff required to make drag/dropping of a shortcut file work on Chicago //--------------------------------------------------------------------------- BOOL ResolveLink( TCHAR * szFileName ); BOOL g_fOleInitialized = FALSE; void CALLBACK ToolTipsTimerFunc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime ); WNDPROC g_lpfnToolTips; #define TOOLTIPS_TIMER_ID 0x5236 #define TOOLTIPS_TIMER_LEN 150 TBBUTTON tbButtons[DEFAULT_TBAR_SIZE] = { { IDX_SEPARATOR, 1, 0, TBSTYLE_SEP }, { IDX_1, IDM_DATABASE_EDIT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0,0, 0, -1 }, { IDX_SEPARATOR, 2, 0, TBSTYLE_SEP }, { IDX_2, IDM_TIME_REMAINING, TBSTATE_CHECKED | TBSTATE_ENABLED, TBSTYLE_CHECK | TBSTYLE_GROUP, 0,0, 0, -1 }, { IDX_3, IDM_TRACK_REMAINING, TBSTATE_ENABLED, TBSTYLE_CHECK | TBSTYLE_GROUP, 0,0, 0, -1 }, { IDX_4, IDM_DISC_REMAINING, TBSTATE_ENABLED, TBSTYLE_CHECK | TBSTYLE_GROUP, 0,0, 0, -1 }, { IDX_SEPARATOR, 3, 0, TBSTYLE_SEP }, { IDX_5, IDM_OPTIONS_RANDOM, TBSTATE_ENABLED, TBSTYLE_CHECK, 0,0, 0, -1 }, { IDX_6, IDM_OPTIONS_MULTI, TBSTATE_ENABLED, TBSTYLE_CHECK, 0,0, 0, -1 }, { IDX_7, IDM_OPTIONS_CONTINUOUS, TBSTATE_ENABLED, TBSTYLE_CHECK, 0,0, 0, -1 }, { IDX_8, IDM_OPTIONS_INTRO, TBSTATE_ENABLED, TBSTYLE_CHECK, 0,0, 0, -1 } }; BITMAPBTN tbPlaybar[] = { { IDX_1, IDM_PLAYBAR_PLAY, 0 }, { IDX_2, IDM_PLAYBAR_PAUSE, 0 }, { IDX_3, IDM_PLAYBAR_STOP, 0 }, { IDX_4, IDM_PLAYBAR_PREVTRACK, 0 }, { IDX_5, IDM_PLAYBAR_SKIPBACK, 0 }, { IDX_6, IDM_PLAYBAR_SKIPFORE, 0 }, { IDX_7, IDM_PLAYBAR_NEXTTRACK, 0 }, { IDX_8, IDM_PLAYBAR_EJECT, 0 } }; /* ** these values are defined by the UI gods... */ const int dxButton = 24; const int dyButton = 22; const int dxBitmap = 16; const int dyBitmap = 16; const int xFirstButton = 8; /* ------------------------------------------------------------------------- ** Try to prevent multiple cdplayers ** ------------------------------------------------------------------------- */ #pragma data_seg(".sdata") int g_iInUse = -1; #pragma data_seg() /******************************Public*Routine******************************\ * WinMain * * * Windows recognizes this function by name as the initial entry point * for the program. This function calls the application initialization * routine, if no other instance of the program is running, and always * calls the instance initialization routine. It then executes a message * retrieval and dispatch loop that is the top-level control structure * for the remainder of execution. The loop is terminated when a WM_QUIT * message is received, at which time this function exits the application * instance by returning the value passed by PostQuitMessage(). * * If this function must abort before entering the message loop, it * returns the conventional value NULL. * * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { MSG msg; BOOL fFirstInstance = (InterlockedIncrement( &g_iInUse ) == 0); HACCEL hAccel; #ifdef DBG /* ** This removes the Gdi batch feature. It ensures that the screen ** is updated after every gdi call - very useful for debugging. */ GdiSetBatchLimit(1); #endif /* ** Save the instance handle in static variable, which will be used in ** many subsequence calls from this application to Windows. */ g_hInst = hInstance; g_lpCmdLine = lpCmdLine; /* ** If CDPlayer is already running try to bring it to the top. ** If we can't find its window it probably means that the it has ** either crashed or not got around to creating it yet. */ if ( !fFirstInstance ) { CdPlayerAlreadyRunning(); if (g_fOleInitialized) { OleUninitialize(); } InterlockedDecrement( &g_iInUse ); return FALSE; } InitializeCriticalSection (&g_csTOCSerialize); /* ** Initialize the cdplayer application. */ CdPlayerStartUp(); /* ** If the "-Play" command line option was specified we need to start ** minimized and non active. */ if (IsPlayOptionGiven( GetCommandLine() )) { nCmdShow = SW_SHOWMINNOACTIVE; } /* ** Try to load the accelerator table for the app */ hAccel = LoadAccelerators( hInstance, MAKEINTRESOURCE(IDR_ACCELTABLE) ); /* ** Make the window visible; update its client area */ ShowWindow( g_hwndApp, nCmdShow ); UpdateWindow( g_hwndApp ); /* ** Acquire and dispatch messages until a WM_QUIT message is received. */ while ( GetMessage( &msg, NULL, 0, 0 ) ) { if ( hAccel && TranslateAccelerator(g_hwndApp, hAccel, &msg) ) { continue; } if ( !IsDialogMessage( g_hwndApp, &msg ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } } // Cleanup DeleteCriticalSection (&g_csTOCSerialize); if (g_fOleInitialized) { OleUninitialize(); } InterlockedDecrement( &g_iInUse ); return msg.wParam; } /*****************************Private*Routine******************************\ * InitInstance * * * This function is called at initialization time for every instance of * this application. This function performs initialization tasks that * cannot be shared by multiple instances. * * In this case, we save the instance handle in a static variable and * create and display the main program window. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ BOOL InitInstance( HANDLE hInstance ) { HWND hwnd; WNDCLASS cls; /* ** Load in some strings */ _tcscpy( g_szArtistTxt, IdStr( STR_HDR_ARTIST ) ); _tcscpy( g_szTitleTxt, IdStr( STR_HDR_TITLE ) ); _tcscpy( g_szUnknownTxt, IdStr( STR_UNKNOWN ) ); _tcscpy( g_szTrackTxt, IdStr( STR_HDR_TRACK ) ); g_szTimeSep[0] = TEXT(':'); g_szTimeSep[1] = g_chNULL; GetLocaleInfo( GetUserDefaultLCID(), LOCALE_STIME, g_szTimeSep, 10 ); /* ** Load the applications icon */ hIconCdPlayer = LoadIcon( hInstance, MAKEINTRESOURCE(IDR_CDPLAYER_ICON) ); g_hbmTrack = LoadBitmap( hInstance, MAKEINTRESOURCE(IDR_TRACK) ); CheckSysColors(); /* ** Initialize the my classes. We do this here because the dialog ** that we are about to create contains two windows on my class. ** The dialog would fail to be created if the classes was not registered. */ g_fDisplayT = TRUE; InitLEDClass( g_hInst ); Init_SJE_TextClass( g_hInst ); cls.lpszClassName = g_szSJE_CdPlayerClass; cls.hCursor = LoadCursor(NULL, IDC_ARROW); cls.hIcon = hIconCdPlayer; cls.lpszMenuName = NULL; cls.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); cls.hInstance = hInstance; cls.style = CS_DBLCLKS; cls.lpfnWndProc = DefDlgProc; cls.cbClsExtra = 0; cls.cbWndExtra = DLGWINDOWEXTRA; if ( !RegisterClass(&cls) ) { return FALSE; } /* ** Create a main window for this application instance. */ hwnd = CreateDialog( g_hInst, MAKEINTRESOURCE(IDR_CDPLAYER), (HWND)NULL, MainWndProc ); /* ** If window could not be created, return "failure" */ if ( !hwnd ) { return FALSE; } g_hwndApp = hwnd; return TRUE; } /*****************************Private*Routine******************************\ * CdPlayerAlreadyRunning * * * * History: * dd-mm-95 - StephenE - Created * \**************************************************************************/ void CdPlayerAlreadyRunning( void ) { COPYDATASTRUCT cpds; HWND hwndFind; DWORD dwForeGndThreadID = 0L; HWND hwndForeGnd; hwndFind = FindWindow( g_szSJE_CdPlayerClass, NULL ); /* ** If the foreground window is an instance of cdplayer don't ** forward the command line to it. This just confuses the user ** as his app suddenly starts play without him pressing play. ** This is only a problem on Chicago when the user inserts a ** a new disc into the drive. */ hwndForeGnd = GetForegroundWindow(); if (hwndForeGnd != NULL) { dwForeGndThreadID = GetWindowThreadProcessId(hwndForeGnd, NULL); } if ( (hwndFind != NULL) && (IsIconic(hwndFind) || GetWindowThreadProcessId(hwndFind, NULL) != dwForeGndThreadID) ) { /* ** First parse the command line to see if the play command has ** been specified. If "play" has been specified then don't bother ** restoring the other instance of CDPlayer. */ if (! IsPlayOptionGiven( GetCommandLine() )) { hwndFind = GetLastActivePopup( hwndFind ); if ( IsIconic( hwndFind ) ) { ShowWindow( hwndFind, SW_RESTORE ); } BringWindowToTop( hwndFind ); SetForegroundWindow( hwndFind ); } /* ** Now transfer our command line to the other instance of ** CDPlayer. We don't do any track/disc validation here but ** rather defer everything to the cdplayer that is already running. */ cpds.dwData = 0L; cpds.cbData = (_tcslen(GetCommandLine()) + 1) * sizeof(TCHAR); cpds.lpData = AllocMemory(cpds.cbData); if (cpds.lpData == NULL) { // Error - not enough memory to continue return; } _tcscpy((LPTSTR)cpds.lpData, GetCommandLine()); SendMessage(hwndFind, WM_COPYDATA, 0, (LPARAM)(LPVOID)&cpds); LocalFree((HLOCAL)cpds.lpData); } else if (hwndFind != NULL) { UINT cch, cchInsert; UINT cchCmd, cchUpdate; LPTSTR lpstr; // Tell primary instance to just update, // In case this CD was just inserted cchUpdate = _tcslen (g_szUpdateOption); cchCmd = _tcslen(GetCommandLine()); cch = cchUpdate + cchCmd; cpds.dwData = 0L; cpds.cbData = (cch + 1) * sizeof(TCHAR); cpds.lpData = AllocMemory(cpds.cbData); if (cpds.lpData == NULL) { // Error - not enough memory to continue return; } // // Insert the -UPDATE option between cdplayer.exe // parameter and rest of parameters // // find insertion point lpstr = (LPTSTR)GetCommandLine(); cchInsert = _tcsspn(lpstr, g_szBlank); lpstr += cchInsert; cchInsert += _tcscspn(lpstr, g_szBlank); lpstr += cchInsert; _tcsncpy((LPTSTR)cpds.lpData, GetCommandLine(), cchInsert); ((LPTSTR)cpds.lpData)[cchInsert] = 0; _tcscat((LPTSTR)cpds.lpData, g_szUpdateOption); cchInsert += cchUpdate; ((LPTSTR)cpds.lpData)[cchInsert] = 0; _tcscat((LPTSTR)cpds.lpData, lpstr); SendMessage(hwndFind, WM_COPYDATA, 0, (LPARAM)(LPVOID)&cpds); LocalFree((HLOCAL)cpds.lpData); } } /*****************************Private*Routine******************************\ * CdPlayerStartUp * * * * History: * dd-mm-95 - StephenE - Created * \**************************************************************************/ void CdPlayerStartUp( void ) { /* ** Reseed random generator */ srand( GetTickCount() ); /* ** Set error mode popups for critical errors (like ** no disc in drive) OFF. */ SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX ); /* ** Scan device chain for CDROM devices... Terminate if none found. */ g_NumCdDevices = ScanForCdromDevices( ); if ( g_NumCdDevices == 0 ) { LPTSTR lpstrTitle; LPTSTR lpstrText; lpstrTitle = AllocMemory( STR_MAX_STRING_LEN * sizeof(TCHAR) ); lpstrText = AllocMemory( STR_MAX_STRING_LEN * sizeof(TCHAR) ); _tcscpy( lpstrText, IdStr(STR_NO_CDROMS) ); _tcscpy( lpstrTitle, IdStr(STR_CDPLAYER) ); MessageBox( NULL, lpstrText, lpstrTitle, MB_APPLMODAL | MB_ICONINFORMATION | MB_OK | MB_SETFOREGROUND ); LocalFree( (HLOCAL)lpstrText ); LocalFree( (HLOCAL)lpstrTitle ); ExitProcess( (UINT)-1 ); } #ifndef USE_IOCTLS // Make sure we have a functional MCI (CD Audio) if (! CheckMCICDA (g_Devices[0]->drive)) { ExitProcess( (UINT)-1 ); } #endif // ! USE_IOCTLS /* ** Perform initializations that apply to a specific instance ** This function actually creates the CdPlayer window. (Note that it is ** not visible yet). If we get here we know that there is a least one ** cdrom device detected which may have a music cd in it. If it does ** contain a music cdrom the table of contents will have been read and ** cd database queryed to determine if the music cd is known. Therefore ** on the WM_INITDIALOG message we should update the "Artist", "Title" and ** "Track" fields of the track info display and adjust the enable state ** of the play buttons. */ if ( !InitInstance( g_hInst ) ) { FatalApplicationError( STR_TERMINATE ); } /* ** Restore ourselves from the ini file */ ReadSettings(); /* ** Scan command the command line. If we were given any valid commandline ** args we have to adjust the nCmdShow parameter. (ie. start minimized ** if the user just wants us to play a certain track. ScanCommandLine can ** overide the default playlist for all the cd-rom devices installed. It ** modifies the global flag g_fPlay and returns the index of the first ** CD-Rom that should be played. */ g_CurrCdrom = g_LastCdrom = 0; /* ** Check to see if the volume controller piglett can be found on ** the path. */ { TCHAR chBuffer[8]; LPTSTR lptstr; g_fVolumeController = (SearchPath( NULL, g_szSndVol32, NULL, 8, chBuffer, &lptstr ) != 0L); } } /*****************************Private*Routine******************************\ * CompleteCdPlayerStartUp * * * * History: * dd-mm-95 - StephenE - Created * \**************************************************************************/ void CompleteCdPlayerStartUp( void ) { int iTrackToSeekTo = -1; int i; /* ** Scan command the command line. If we were given any valid ** commandline args we have to adjust the nCmdShow parameter. (ie. ** start minimized if the user just wants us to play a certain ** track. ScanCommandLine can overide the default playlist for all ** the cd-rom devices installed. It modifies the global flag ** g_fPlay and returns the index of the first CD-Rom that should be ** played. ** */ g_CurrCdrom = g_LastCdrom = ParseCommandLine( GetCommandLine(), &iTrackToSeekTo, FALSE ); /* ** If the message box prompting the user to insert the correct cd disc in ** the drive was displayed, ParseCommandLine will return -1, in which case ** find the most suitable drive, also make sure that we don't come up ** playing. */ if (g_LastCdrom == -1) { g_fPlay = FALSE; g_CurrCdrom = g_LastCdrom = FindMostSuitableDrive(); } /* ** If there was no commandline specifed and there is more ** than one drive available try to select the best drive to make the ** current drive. ** ** We should choose the first disc that is playing if any are ** playing. ** ** Else we should choose the first disc with a music disk in ** it if there any drives with music discs in them. ** ** Else we should chose the first drive that is available if ** any of the drives are available. ** ** Else just choose the first (ie. zeroth) drive. */ if (g_lpCmdLine && !*g_lpCmdLine && g_NumCdDevices > 1) { g_CurrCdrom = g_LastCdrom = FindMostSuitableDrive(); } for ( i = 0; i < g_NumCdDevices; i++) { TimeAdjustInitialize( i ); } /* ** All the rescan threads are either dead or in the act of dying. ** It is now safe to initalize the time information for each ** cdrom drive. */ if ( iTrackToSeekTo != -1 ) { PTRACK_PLAY tr; tr = PLAYLIST( g_CurrCdrom ); if ( tr != NULL ) { for( i = 0; i < iTrackToSeekTo; i++, tr = tr->nextplay ); TimeAdjustSkipToTrack( g_CurrCdrom, tr ); } } /* ** if we are in random mode, then we need to shuffle the play lists. */ if (!g_fSelectedOrder) { ComputeAndUseShufflePlayLists(); } SetPlayButtonsEnableState(); /* ** Start the heart beat time. This timer is responsible for: ** 1. detecting new or ejected cdroms. ** 2. flashing the LED display if we are in paused mode. ** 3. Incrementing the LED display if we are in play mode. */ SetTimer( g_hwndApp, HEARTBEAT_TIMER_ID, HEARTBEAT_TIMER_RATE, HeartBeatTimerProc ); if ( g_fPlay ) { CdPlayerPlayCmd(); } /* ** Make sure that the focus is on a suitable control */ if (g_State & (CD_NO_CD | CD_DATA_CD_LOADED)) { SetFocus( g_hwndControls[INDEX(IDM_PLAYBAR_EJECT)] ); } else if ((g_State & CD_IN_USE) == 0) { if (g_State & (CD_PLAYING | CD_PAUSED)) { SetFocus( g_hwndControls[INDEX(IDM_PLAYBAR_PAUSE)] ); } else { SetFocus( g_hwndControls[INDEX(IDM_PLAYBAR_PLAY)] ); } } } /******************************Public*Routine******************************\ * MainWndProc * * Use the message crackers to dispatch the dialog messages to appropirate * message handlers. The message crackers are portable between 16 and 32 * bit versions of Windows. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ BOOL CALLBACK MainWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { switch ( message ) { HANDLE_MSG( hwnd, WM_INITDIALOG, CDPlay_OnInitDialog ); HANDLE_MSG( hwnd, WM_INITMENUPOPUP, CDPlay_OnInitMenuPopup ); HANDLE_MSG( hwnd, WM_SYSCOLORCHANGE, CDPlay_OnSysColorChange ); HANDLE_MSG( hwnd, WM_DRAWITEM, CDPlay_OnDrawItem ); HANDLE_MSG( hwnd, WM_COMMAND, CDPlay_OnCommand ); HANDLE_MSG( hwnd, WM_DESTROY, CDPlay_OnDestroy ); HANDLE_MSG( hwnd, WM_SIZE, CDPlay_OnSize ); HANDLE_MSG( hwnd, WM_ENDSESSION, CDPlay_OnEndSession ); HANDLE_MSG( hwnd, WM_WININICHANGE, CDPlay_OnWinIniChange ); HANDLE_MSG( hwnd, WM_CTLCOLORSTATIC, Common_OnCtlColor ); HANDLE_MSG( hwnd, WM_CTLCOLORDLG, Common_OnCtlColor ); HANDLE_MSG( hwnd, WM_MEASUREITEM, Common_OnMeasureItem ); HANDLE_MSG( hwnd, WM_NOTIFY, CDPlay_OnNotify ); HANDLE_MSG( hwnd, WM_MENUSELECT, CDPlay_OnMenuSelect ); HANDLE_MSG( hwnd, WM_LBUTTONDBLCLK, CDPlay_OnLButtonDown ); HANDLE_MSG( hwnd, WM_NCHITTEST, CDPlay_OnNCHitTest ); HANDLE_MSG( hwnd, WM_PAINT, CDPlay_OnPaint ); HANDLE_MSG( hwnd, WM_DROPFILES, CDPlay_OnDropFiles ); case WM_DEVICECHANGE: return CDPlay_OnDeviceChange (hwnd, wParam, lParam); case WM_ACTIVATEAPP: if ((BOOL)wParam) { if (g_State & CD_PLAYING) { SetFocus( g_hwndControls[INDEX(IDM_PLAYBAR_STOP)] ); } else { SetFocus( g_hwndControls[INDEX(IDM_PLAYBAR_PLAY)] ); } } return 1; case WM_ERASEBKGND: return 1; case WM_CLOSE: return CDPlay_OnClose(hwnd, FALSE); case WM_NCLBUTTONDBLCLK: if (g_fTitlebarShowing) { return DefWindowProc( hwnd, message, wParam, lParam ); } return HANDLE_WM_NCLBUTTONDBLCLK( hwnd, wParam, lParam, CDPlay_OnLButtonDown ); case WM_COPYDATA: return CDPlay_CopyData( hwnd, (PCOPYDATASTRUCT)lParam ); case WM_CDPLAYER_COPYDATA: return CDPlay_OnCopyData( hwnd, (PCOPYDATASTRUCT)lParam ); case WM_NOTIFY_TOC_READ: return CDPlay_OnTocRead( (int)wParam ); default: return FALSE; } } /*****************************Private*Routine******************************\ * CDPlay_OnInitDialog * * * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ BOOL CDPlay_OnInitDialog( HWND hwnd, HWND hwndFocus, LPARAM lParam ) { LOGFONT lf; int iLogPelsY; HDC hdc; int i; hdc = GetDC( hwnd ); iLogPelsY = GetDeviceCaps( hdc, LOGPIXELSY ); ReleaseDC( hwnd, hdc ); ZeroMemory( &lf, sizeof(lf) ); #ifdef JAPAN //fix kksuzuka: #2238 lf.lfHeight = (-10 * iLogPelsY) / 72; /* 10pt */ lf.lfWeight = 400; /* normal */ lf.lfCharSet = SHIFTJIS_CHARSET; #else lf.lfHeight = (-8 * iLogPelsY) / 72; /* 8pt */ lf.lfWeight = 400; /* normal */ lf.lfCharSet = ANSI_CHARSET; #endif lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = PROOF_QUALITY; lf.lfPitchAndFamily = DEFAULT_PITCH | FF_SWISS; _tcscpy( lf.lfFaceName, g_szAppFontName ); g_hDlgFont = CreateFontIndirect(&lf); #ifdef DAYTONA if (g_hDlgFont) { SendDlgItemMessage( hwnd, IDC_ARTIST_NAME, WM_SETFONT, (WPARAM)(g_hDlgFont), 0L ); SendDlgItemMessage( hwnd, IDC_TITLE_NAME, WM_SETFONT, (WPARAM)(g_hDlgFont), 0L ); SendDlgItemMessage( hwnd, IDC_TRACK_LIST, WM_SETFONT, (WPARAM)(g_hDlgFont), 0L ); } #endif cyMenuCaption = GetSystemMetrics( SM_CYMENU ) + GetSystemMetrics( SM_CYCAPTION ); g_hBrushBkgd = CreateSolidBrush( rgbFace ); BtnCreateBitmapButtons( hwnd, g_hInst, IDR_PLAYBAR, BBS_TOOLTIPS, tbPlaybar, NUM_OF_BUTTONS, 16, 15 ); /* ** Before I go off creating toolbars and status bars ** I need to create a list of all the child windows positions ** so that I can manipulate them when displaying the toolbar, ** status bar and disk info. */ EnumChildWindows( hwnd, ChildEnumProc, (LPARAM)hwnd ); AdjustChildButtons(hwnd); EnumChildWindows( hwnd, ChildEnumProc, (LPARAM)hwnd ); if ( CreateToolbarsAndStatusbar(hwnd) == FALSE ) { /* ** No toolbar - no application, simple ! */ FatalApplicationError( STR_FAIL_INIT ); } DragAcceptFiles( hwnd, TRUE ); /* ** Initialize and read the TOC for all the detected CD-ROMS */ SetPlayButtonsEnableState(); for ( i = 0; i < g_NumCdDevices; i++ ) { ASSERT(g_Devices[i]->State == CD_BEING_SCANNED); ASSERT(g_Devices[i]->hCd == 0L); TimeAdjustInitialize( i ); g_Devices[i]->State = CD_NO_CD; RescanDevice( hwnd, i ); } return FALSE; } #ifdef DAYTONA /*****************************Private*Routine******************************\ * CDPlay_OnPaint * * Paints the hilight under the menu but only if the toolbar is NOT visible. * I use IsWindowVisible to get the TRUE visiblity status of the toolbar. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void CDPlay_OnPaint( HWND hwnd ) { HDC hdc; PAINTSTRUCT ps; RECT rc; hdc = BeginPaint( hwnd, &ps ); CheckSysColors(); GetClientRect( hwnd, &rc ); if (!IsWindowVisible( g_hwndToolbar)) { PatB( hdc, 0, 0, rc.right, 1, rgbHilight ); ExcludeClipRect( hdc, 0, 0, rc.right, 1 ); } if ( ps.fErase ) { DefWindowProc( hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0 ); } EndPaint( hwnd, &ps ); } #endif /*****************************Private*Routine******************************\ * CDPlay_OnInitMenuPopup * * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void CDPlay_OnInitMenuPopup( HWND hwnd, HMENU hMenu, UINT item, BOOL fSystemMenu ) { switch ( item ) { case 0: /* ** Do we have a music cd loaded. */ if (g_State & (CD_BEING_SCANNED | CD_IN_USE | CD_NO_CD | CD_DATA_CD_LOADED)) { EnableMenuItem(hMenu, IDM_DATABASE_EDIT, MF_GRAYED | MF_BYCOMMAND); } else { EnableMenuItem(hMenu, IDM_DATABASE_EDIT, MF_ENABLED | MF_BYCOMMAND); } break; case 1: CheckMenuItemIfTrue( hMenu, IDM_VIEW_STATUS, g_fStatusbarVisible ); CheckMenuItemIfTrue( hMenu, IDM_VIEW_TRACKINFO, g_fTrackInfoVisible ); CheckMenuItemIfTrue( hMenu, IDM_VIEW_TOOLBAR, g_fToolbarVisible ); CheckMenuItemIfTrue( hMenu, IDM_TIME_REMAINING, g_fDisplayT ); CheckMenuItemIfTrue( hMenu, IDM_TRACK_REMAINING, g_fDisplayTr ); CheckMenuItemIfTrue( hMenu, IDM_DISC_REMAINING, g_fDisplayDr ); if (g_fVolumeController) { EnableMenuItem(hMenu, IDM_VIEW_VOLUME, MF_ENABLED | MF_BYCOMMAND); } else { EnableMenuItem(hMenu, IDM_VIEW_VOLUME, MF_GRAYED | MF_BYCOMMAND); } break; case 2: CheckMenuItemIfTrue( hMenu, IDM_OPTIONS_RANDOM, !g_fSelectedOrder ); //if (g_fMultiDiskAvailable) { // CheckMenuItemIfTrue( hMenu, IDM_OPTIONS_MULTI, !g_fSingleDisk ); //} CheckMenuItemIfTrue( hMenu, IDM_OPTIONS_CONTINUOUS, g_fContinuous ); CheckMenuItemIfTrue( hMenu, IDM_OPTIONS_INTRO, g_fIntroPlay ); break; } } /*****************************Private*Routine******************************\ * CDPlay_OnSysColorChange * * * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void CDPlay_OnSysColorChange( HWND hwnd ) { CheckSysColors(); if (g_hBrushBkgd) { DeleteObject(g_hBrushBkgd); g_hBrushBkgd = CreateSolidBrush( rgbFace ); } BtnUpdateColors( hwnd ); FORWARD_WM_SYSCOLORCHANGE(g_hwndToolbar, SendMessage); } /*****************************Private*Routine******************************\ * CDPlay_OnWinIniChange * * Updates the time format separator and the LED display * * History: * 29-09-94 - StephenE - Created * \**************************************************************************/ void CDPlay_OnWinIniChange( HWND hwnd, LPCTSTR lpszSectionName ) { int cy; RECT rc; cy = GetSystemMetrics( SM_CYMENU ) + GetSystemMetrics( SM_CYCAPTION ); if ( cy != cyMenuCaption ) { GetWindowRect( hwnd, &rc ); rc.bottom += cy - cyMenuCaption; SetWindowPos( hwnd, HWND_TOP, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER ); cyMenuCaption = cy; } GetLocaleInfo( GetUserDefaultLCID(), LOCALE_STIME, g_szTimeSep, 10 ); UpdateDisplay( DISPLAY_UPD_LED | DISPLAY_UPD_DISC_TIME | DISPLAY_UPD_TRACK_TIME ); } /*****************************Private*Routine******************************\ * CDPlay_OnDrawItem * * * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ BOOL CDPlay_OnDrawItem( HWND hwnd, const DRAWITEMSTRUCT *lpdis ) { int i; i = INDEX(lpdis->CtlID); switch (lpdis->CtlType) { case ODT_BUTTON: /* ** See if the fast foreward or backward buttons has been pressed or ** released. If so execute the seek command here. Do nothing on ** the WM_COMMAND message. */ if ( lpdis->CtlID == IDM_PLAYBAR_SKIPBACK || lpdis->CtlID == IDM_PLAYBAR_SKIPFORE ) { if (lpdis->itemAction & ODA_SELECT ) { g_AcceleratorCount = 0; CdPlayerSeekCmd( hwnd, (lpdis->itemState & ODS_SELECTED), lpdis->CtlID ); } } /* ** Now draw the button according to the buttons state information. */ tbPlaybar[i].fsState = LOBYTE(lpdis->itemState); if (lpdis->itemAction & (ODA_DRAWENTIRE | ODA_SELECT)) { BtnDrawButton( hwnd, lpdis->hDC, (int)lpdis->rcItem.right, (int)lpdis->rcItem.bottom, &tbPlaybar[i] ); } else if (lpdis->itemAction & ODA_FOCUS) { BtnDrawFocusRect(lpdis->hDC, &lpdis->rcItem, lpdis->itemState); } return TRUE; case ODT_COMBOBOX: if (lpdis->itemAction & (ODA_DRAWENTIRE | ODA_SELECT)) { switch (lpdis->CtlID) { case IDC_ARTIST_NAME: DrawDriveItem( lpdis->hDC, &lpdis->rcItem, lpdis->itemData, (ODS_SELECTED & lpdis->itemState) ); break; case IDC_TRACK_LIST: DrawTrackItem( lpdis->hDC, &lpdis->rcItem, lpdis->itemData, (ODS_SELECTED & lpdis->itemState) ); break; } } return TRUE; } return FALSE; } /*****************************Private*Routine******************************\ * CDPlay_OnCommand * * * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void CDPlay_OnCommand( HWND hwnd, int id, HWND hwndCtl, UINT codeNotify ) { /* ** Comobo box notification ? */ int index, i; HWND hwndCtrl; PTRACK_PLAY tr; switch( id ) { case IDC_TRACK_LIST: switch (codeNotify) { case CBN_SELENDOK: hwndCtrl = g_hwndControls[INDEX(IDC_TRACK_LIST)]; index = SendMessage( hwndCtrl, CB_GETCURSEL, 0, 0L ); tr = PLAYLIST( g_CurrCdrom ); if ( tr != NULL ) { PTRACK_PLAY trCurrent = CURRTRACK(g_CurrCdrom); for( i = 0; tr && (i < index); i++, tr = tr->nextplay ); if (tr != trCurrent) { TimeAdjustSkipToTrack( g_CurrCdrom, tr ); } } break; case CBN_CLOSEUP: { // Here we have to check that the track currently playing // is actually the same track thats selected in the combo box. PTRACK_PLAY trCurrent = CURRTRACK(g_CurrCdrom); hwndCtrl = g_hwndControls[INDEX(IDC_TRACK_LIST)]; index = SendMessage( hwndCtrl, CB_GETCURSEL, 0, 0L ); tr = PLAYLIST( g_CurrCdrom ); if (tr != NULL && trCurrent != NULL) { for( i = 0; tr && (i < index); i++, tr = tr->nextplay ); // If tr and trCurrent don't point to the same thing // then we need to change the combo boxes selection. if ( tr != trCurrent ) { for( i = 0, tr = PLAYLIST( g_CurrCdrom ); tr && tr != trCurrent; i++, tr = tr->nextplay ); if ( tr != NULL ) { ComboBox_SetCurSel( hwndCtrl, i); } } } break; } } break; case IDC_ARTIST_NAME: if (codeNotify == CBN_SELCHANGE) { i = g_CurrCdrom; hwndCtrl = g_hwndControls[INDEX(IDC_ARTIST_NAME)]; index = SendMessage( hwndCtrl, CB_GETCURSEL, 0, 0L ); SwitchToCdrom( index, TRUE ); SetPlayButtonsEnableState(); if ( g_CurrCdrom == i ) { SendMessage( hwndCtrl, CB_SETCURSEL, (WPARAM)i, 0 ); } } break; case IDM_VIEW_VOLUME: { HANDLE hThread; DWORD dwThreadId; /* ** We WinExec sndvol on a separate thread because winexec ** is a potentially lengthy call. If we are playing a cd ** when we try to start sndvol the LED display freezes ** for a short time this looks real ugly. */ hThread = CreateThread( NULL, 0L, (LPTHREAD_START_ROUTINE)StartSndVol, NULL, 0L, &dwThreadId ); if ( hThread != INVALID_HANDLE_VALUE ) { CloseHandle( hThread ); } } break; case IDM_VIEW_TOOLBAR: ShowToolbar(); break; case IDM_VIEW_STATUS: ShowStatusbar(); break; case IDM_VIEW_TRACKINFO: ShowTrackInfo(); break; case IDM_OPTIONS_RANDOM: if ( LockALLTableOfContents() ) { FlipBetweenShuffleAndOrder(); g_fSelectedOrder = !g_fSelectedOrder; } break; //case IDM_OPTIONS_MULTI: //g_fSingleDisk = !g_fSingleDisk; //break; case IDM_OPTIONS_INTRO: g_fIntroPlay = !g_fIntroPlay; break; case IDM_OPTIONS_CONTINUOUS: g_fContinuous = !g_fContinuous; break; case IDM_OPTIONS_PREFERENCES: DialogBox( g_hInst, MAKEINTRESOURCE(IDR_PREFERENCES), hwnd, PreferencesDlgProc ); break; case IDM_TIME_REMAINING: g_fDisplayT = TRUE; g_fDisplayTr = g_fDisplayDr = FALSE; if (codeNotify == 0) { UpdateToolbarTimeButtons(); } UpdateDisplay( DISPLAY_UPD_LED ); break; case IDM_TRACK_REMAINING: g_fDisplayTr = TRUE; g_fDisplayDr = g_fDisplayT = FALSE; if (codeNotify == 0) { UpdateToolbarTimeButtons(); } UpdateDisplay( DISPLAY_UPD_LED ); break; case IDM_DISC_REMAINING: g_fDisplayDr = TRUE; g_fDisplayTr = g_fDisplayT = FALSE; if (codeNotify == 0) { UpdateToolbarTimeButtons(); } UpdateDisplay( DISPLAY_UPD_LED ); break; case IDM_PLAYBAR_EJECT: CdPlayerEjectCmd(); break; case IDM_PLAYBAR_PLAY: /* ** If we currently in PLAY mode and the command came from ** a keyboard accelerator then assume that the user really ** means Pause. This is because the Ctrl-P key sequence ** is a toggle between Play and Paused. codeNotify is 1 when ** the WM_COMMAND message came from an accelerator and 0 when ** it cam from a menu. */ if ((g_State & CD_PLAYING) && (codeNotify == 1)) { CdPlayerPauseCmd(); } else { CdPlayerPlayCmd(); } break; case IDM_PLAYBAR_PAUSE: CdPlayerPauseCmd(); break; case IDM_PLAYBAR_STOP: CdPlayerStopCmd(); break; case IDM_PLAYBAR_PREVTRACK: CdPlayerPrevTrackCmd(); break; case IDM_PLAYBAR_NEXTTRACK: CdPlayerNextTrackCmd(); break; case IDM_DATABASE_EXIT: PostMessage( hwnd, WM_CLOSE, 0, 0L ); break; case IDM_DATABASE_EDIT: CdDiskInfoDlg(); break; case IDM_HELP_TOPICS: WinHelp(hwnd, g_HelpFileName, HELP_FINDER, 0); break; case IDM_HELP_ABOUT: ShellAbout( hwnd, IdStr(STR_CDPLAYER), g_szEmpty, hIconCdPlayer ); break; } UpdateToolbarButtons(); } /******************************Public*Routine******************************\ * CDPlay_OnDestroy * * * * History: * dd-mm-93 - StephenE - Created * \**************************************************************************/ void CDPlay_OnDestroy( HWND hwnd ) { int i; for ( i = 0; i < g_NumCdDevices; i++ ) { if (g_fStopCDOnExit) { if ( g_Devices[i]->State & CD_PLAYING || g_Devices[i]->State & CD_PAUSED ) { StopTheCdromDrive( i ); } } #ifdef USE_IOCTLS if ( g_Devices[i]->hCd != NULL ) { CloseHandle( g_Devices[i]->hCd ); } #else if ( g_Devices[i]->hCd != 0L ) { CloseCdRom( g_Devices[i]->hCd ); g_Devices[i]->hCd = 0L; } #endif LocalFree( (HLOCAL) g_Devices[i] ); } if (g_hBrushBkgd) { DeleteObject( g_hBrushBkgd ); } if ( g_hDlgFont ) { DeleteObject( g_hDlgFont ); } WinHelp( hwnd, g_HelpFileName, HELP_QUIT, 0 ); PostQuitMessage( 0 ); } /******************************Public*Routine******************************\ * CDPlay_OnClose * * * * History: * dd-mm-93 - StephenE - Created * \**************************************************************************/ BOOL CDPlay_OnClose( HWND hwnd, BOOL fShuttingDown ) { /* ** If we are playing or paused and the brain dead "don't stop playing ** on exit" flag set, then we need to tell the user that he is about ** to go into stupid mode. Basically CD Player can only perform as expected ** if the user has not mucked about with the play list, hasn't put the ** app into random mode or intro play mode or continuous play mode or ** multi-disc mode. */ if ( !fShuttingDown && !g_fStopCDOnExit && (g_State & (CD_PLAYING | CD_PAUSED) ) ) { if ( !g_fSelectedOrder || g_fIntroPlay || g_fContinuous || !g_fSingleDisk || !PlayListMatchesAvailList() ) { TCHAR s1[256]; TCHAR s2[256]; int iMsgBoxRtn; _tcscpy( s1, IdStr( STR_EXIT_MESSAGE ) ); _tcscpy( s2, IdStr( STR_CDPLAYER ) ); iMsgBoxRtn = MessageBox( g_hwndApp, s1, s2, MB_APPLMODAL | MB_DEFBUTTON1 | MB_ICONQUESTION | MB_YESNO); if ( iMsgBoxRtn == IDNO ) { return TRUE; } } } WriteSettings(); return DestroyWindow( hwnd ); } /*****************************Private*Routine******************************\ * CDPlay_OnEndSession * * If the session is really ending make sure that we stop the CD Player * from playing and that all the ini file stuff is saved away. * * History: * dd-mm-93 - StephenE - Created * \**************************************************************************/ void CDPlay_OnEndSession( HWND hwnd, BOOL fEnding ) { if ( fEnding ) { CDPlay_OnClose( hwnd, fEnding ); } } /******************************Public*Routine******************************\ * CDPlay_OnSize * * * * History: * dd-mm-93 - StephenE - Created * \**************************************************************************/ void CDPlay_OnSize( HWND hwnd, UINT state, int cx, int cy ) { if (g_fIsIconic && (state != SIZE_MINIMIZED)) { SetWindowText( hwnd, IdStr( STR_CDPLAYER ) ); } g_fIsIconic = (state == SIZE_MINIMIZED); if (IsWindow(g_hwndStatusbar)) { SendMessage( g_hwndStatusbar, WM_SIZE, 0, 0L ); } if (IsWindow(g_hwndToolbar)) { SendMessage( g_hwndToolbar, WM_SIZE, 0, 0L ); } } /*****************************Private*Routine******************************\ * CDPlay_OnNotify * * Time to display the little tool tips. Also, change the status bar * so that it displays a longer version of the tool tip text. * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ LRESULT CDPlay_OnNotify( HWND hwnd, int idFrom, NMHDR *pnmhdr ) { switch (pnmhdr->code) { case TTN_NEEDTEXT: { LPTOOLTIPTEXT lpTt; LPTSTR lpstr; UINT msgId; lpTt = (LPTOOLTIPTEXT)pnmhdr; msgId = lpTt->hdr.idFrom; /* ** If we are paused, the pause button will cause the device ** to play. So we change the ToolTip text to show "Play" ** when the cursor is over "Pause". We should not need to ** check that a music CD is loaded because the CD_PAUSED ** state can only be set after Play starts. */ if ( (g_State & CD_PAUSED) && (msgId == IDM_PLAYBAR_PAUSE || msgId == IDM_PLAYBAR_PLAY)) { msgId = IDM_PLAYBAR_RESUME; } LoadString( g_hInst, msgId, lpTt->szText, sizeof(lpTt->szText) ); lpstr = IdStr(lpTt->hdr.idFrom + MENU_STRING_BASE); if (*lpstr) { SendMessage( g_hwndStatusbar, SB_SETTEXT, SBT_NOBORDERS|255, (LPARAM)lpstr ); SendMessage(g_hwndStatusbar, SB_SIMPLE, 1, 0L); UpdateWindow(g_hwndStatusbar); } } break; } return TRUE; } /*****************************Private*Routine******************************\ * CDPlay_OnNCHitTest * * Here we pretend that the client area is really the caption if we are in * mini-mode. This allows the user to move the player to a new position. * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ UINT CDPlay_OnNCHitTest( HWND hwnd, int x, int y ) { UINT ht = FORWARD_WM_NCHITTEST(hwnd, x, y, DefWindowProc ); if (!g_fTitlebarShowing && (ht == HTCLIENT) ) { ht = HTCAPTION; } SetWindowLong(hwnd, DWL_MSGRESULT, ht); return ht; } /*****************************Private*Routine******************************\ * CDPlay_OnLButtonDown * * This function processes the WM_LBUTTONDBLCLK and WM_NCLBUTTONDBLCLK * messages. Here we determine if it is possible for CD Player to go * into mini-mode or whether CD Player should be restored from min-mode. * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ void CDPlay_OnLButtonDown( HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags ) { static UINT uID; const DWORD dwTransientStyle = (WS_BORDER | WS_SYSMENU | WS_MINIMIZEBOX); DWORD dwStyle; RECT rc; int xpos, ypos, dx, dy; if (!(g_fToolbarVisible || g_fStatusbarVisible || g_fTrackInfoVisible)) { dwStyle = GetWindowLong( hwnd, GWL_STYLE ); GetWindowRect( hwnd, &rc ); xpos = rc.left; dx = rc.right - rc.left; if (g_fTitlebarShowing) { dwStyle &= ~dwTransientStyle; uID = SetWindowLong( hwnd, GWL_ID, 0 ); ypos = rc.top + cyMenuCaption; dy = (rc.bottom - rc.top) - cyMenuCaption; } else { dwStyle |= dwTransientStyle; SetWindowLong( hwnd, GWL_ID, uID ); ypos = rc.top - cyMenuCaption; dy = (rc.bottom - rc.top) + cyMenuCaption; } if (g_fTitlebarShowing) { DWORD dwExStyle; dwExStyle = GetWindowLong( hwnd, GWL_EXSTYLE ); dwExStyle |= WS_EX_WINDOWEDGE; SetWindowLong( hwnd, GWL_STYLE, dwStyle ); SetWindowLong( hwnd, GWL_EXSTYLE, dwExStyle ); } else { SetWindowLong( hwnd, GWL_STYLE, dwStyle ); } g_fTitlebarShowing = !g_fTitlebarShowing; SetWindowPos( hwnd, NULL, xpos, ypos, dx, dy, SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED ); } } BOOL CDPlay_CopyData( HWND hwnd, PCOPYDATASTRUCT lpcpds ) { LPTSTR lpCmdLine; // Make a copy of the passed command line as we are not supposed // to write into the one passed in the WM_COPYDATA message. lpCmdLine = AllocMemory( lpcpds->cbData ); _tcscpy( lpCmdLine, (LPCTSTR)lpcpds->lpData ); PostMessage (hwnd, WM_CDPLAYER_COPYDATA, 0, (LPARAM)(LPVOID)lpCmdLine); return TRUE; } // End CopyData /*****************************Private*Routine******************************\ * CDPlay_OnCopyData * * Handles command lines passed from other intances of CD Player * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ BOOL CDPlay_OnCopyData( HWND hwnd, PCOPYDATASTRUCT lpcpds ) { LPTSTR lpCmdLine; BOOL fWasPlaying = FALSE; BOOL fUpdate; int iTrack = -1; int iCdRom; // Prevent Re-entrancy while // we are opening/closing CD's if (g_fInCopyData) return FALSE; g_fInCopyData = TRUE; /* ** Make a copy of the passed command line as we are not supposed ** to write into the one passed in the WM_COPYDATA message. */ //lpCmdLine = AllocMemory( lpcpds->cbData ); //_tcscpy( lpCmdLine, (LPCTSTR)lpcpds->lpData ); lpCmdLine = (LPTSTR)(LPVOID)lpcpds; if (lpCmdLine == NULL) { g_fInCopyData = FALSE; return 0L; } iCdRom = ParseCommandLine( lpCmdLine, &iTrack, FALSE ); if (iCdRom < 0 && iTrack < 0) { LocalFree( (HLOCAL)lpCmdLine ); g_fInCopyData = FALSE; return 0L; } // Check if it is just an update command?!? fUpdate = IsUpdateOptionGiven (lpCmdLine); if ((fUpdate) && (iTrack == -1)) { if ((iCdRom >= 0) && (iCdRom < g_NumCdDevices)) { CheckUnitCdrom(iCdRom, TRUE); } LocalFree( (HLOCAL)lpCmdLine ); g_fInCopyData = FALSE; return 0L; } /* ** Remember our current playing state as we need to temporarly ** stop the CD if it is currently playing. */ if ( g_State & (CD_PLAYING | CD_PAUSED) ) { #ifdef DBG dprintf("Auto Stopping"); #endif while( !LockALLTableOfContents() ) { MSG msg; #if DBG dprintf("Busy waiting for TOC to become valid!"); #endif GetMessage( &msg, NULL, WM_NOTIFY_TOC_READ, WM_NOTIFY_TOC_READ ); DispatchMessage( &msg ); } CdPlayerStopCmd(); fWasPlaying = TRUE; } /* ** Figure what has been passed and act on it accordingly. */ HandlePassedCommandLine( lpCmdLine, TRUE ); /* ** If we were playing make sure that we are still playing the ** new track(s) */ if ( fWasPlaying || g_fPlay ) { #ifdef DBG dprintf("Trying to autoplay"); #endif while( !LockTableOfContents(g_CurrCdrom) ) { MSG msg; #ifdef DBG dprintf("Busy waiting for TOC to become valid!"); #endif GetMessage( &msg, NULL, WM_NOTIFY_TOC_READ, WM_NOTIFY_TOC_READ ); DispatchMessage( &msg ); } CdPlayerPlayCmd(); } /* ** Free the local copy of the command line. */ LocalFree( (HLOCAL)lpCmdLine ); g_fInCopyData = FALSE; return 0L; } /*****************************Private*Routine******************************\ * CDPlay_OnTocRead * * * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ BOOL CDPlay_OnTocRead( int iDriveRead ) { static int iNumRead = 0; // This serializes processing between this // function and the various Table of Content Threads // Preventing resource contention on CDROM Multi-Changers. EnterCriticalSection (&g_csTOCSerialize); /* ** Have we finished the initial read of the CD-Rom TOCs ? ** If so we have to re-open the device. We only need to do this ** on Daytona because MCI device handles are not shared between threads. */ iNumRead++; #ifndef USE_IOCTLS #ifdef DAYTONA if (iNumRead <= g_NumCdDevices) { /* ** Now, open the cdrom device on the UI thread. */ g_Devices[iDriveRead]->hCd = OpenCdRom( g_Devices[iDriveRead]->drive, NULL ); } #endif #endif /* ** This means that one of the threads dedicated to reading the ** toc has finished. iDriveRead contains the relevant cdrom id. */ LockALLTableOfContents(); if ( g_Devices[iDriveRead]->State & CD_LOADED ) { /* ** We have a CD loaded, so generate unique ID ** based on TOC information. */ g_Devices[iDriveRead]->CdInfo.Id = ComputeNewDiscId( iDriveRead ); /* ** Check database for this compact disc */ AddFindEntry( iDriveRead, g_Devices[iDriveRead]->CdInfo.Id, &(g_Devices[iDriveRead]->toc) ); } /* ** If we have completed the initialization of the Cd-Rom drives we can ** now complete the startup processing of the application. */ if (iNumRead == g_NumCdDevices) { CompleteCdPlayerStartUp(); } else { /* ** if we are in random mode, then we need to shuffle the play lists. ** but only if we can lock all the cd devices. */ TimeAdjustInitialize( iDriveRead ); if ( g_fSelectedOrder == FALSE ) { if ( LockALLTableOfContents() ) { ComputeAndUseShufflePlayLists(); } } ComputeDriveComboBox(); if (iDriveRead == g_CurrCdrom) { SetPlayButtonsEnableState(); } } LeaveCriticalSection (&g_csTOCSerialize); return TRUE; } /*****************************Private*Routine******************************\ * SubClassedToolTips * * If the tooltips window is being hidden we need to reset the status bar * to its normal display. In all instance we pass the message on the * the tooltip window proc for processing. This is all done by use of a timer. * If the timer fires it means that the tooltips have been invisible for * a reasonable length of time. We always kill the timer when the tooltips * are about to be displayed, this means that if the user is displaying the * tips quickly we won't see the annoying flicker. * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ LRESULT CALLBACK SubClassedToolTips( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { if ( (uMsg == WM_SHOWWINDOW) && ((BOOL)wParam == FALSE) ) { SetTimer( g_hwndApp, TOOLTIPS_TIMER_ID, TOOLTIPS_TIMER_LEN, ToolTipsTimerFunc ); } else if ( uMsg == WM_MOVE ) { KillTimer( g_hwndApp, TOOLTIPS_TIMER_ID ); } return CallWindowProc(g_lpfnToolTips, hwnd, uMsg, wParam, lParam); } /*****************************Private*Routine******************************\ * ToolTipsTimerFunc * * If this timer fires it means its time to remove the tooltips text on * the status bar. * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ void CALLBACK ToolTipsTimerFunc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime ) { KillTimer( g_hwndApp, TOOLTIPS_TIMER_ID ); SendMessage(g_hwndStatusbar, SB_SIMPLE, 0, 0L); } /*****************************Private*Routine******************************\ * CDPlay_OnMenuSelect * * * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ void CDPlay_OnMenuSelect( HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags ) { TCHAR szString[STR_MAX_STRING_LEN + 1]; /* ** Is it time to end the menu help ? */ if ( (flags == 0xFFFFFFFF) && (hmenu == NULL) ) { SendMessage(g_hwndStatusbar, SB_SIMPLE, 0, 0L); } /* ** Do we have a separator, popup or the system menu ? */ else if ( flags & MF_POPUP ) { SendMessage(g_hwndStatusbar, SB_SIMPLE, 0, 0L); } else if (flags & MF_SYSMENU) { if (flags & MF_SEPARATOR) { szString[0] = g_chNULL; } else { int id = -1; switch (item) { case SC_RESTORE: id = STR_SYSMENU_RESTORE; break; case SC_MOVE: id = STR_SYSMENU_MOVE; break; case SC_SIZE: id = STR_SYSMENU_SIZE; break; case SC_MINIMIZE: id = STR_SYSMENU_MINIMIZE; break; case SC_MAXIMIZE: id = STR_SYSMENU_MAXIMIZE; break; case SC_CLOSE: id = STR_SYSMENU_CLOSE; break; #ifdef DAYTONA case SC_TASKLIST: id = STR_SYSMENU_SWITCH; break; #endif } _tcscpy( szString, IdStr(id) ); } SendMessage( g_hwndStatusbar, SB_SETTEXT, SBT_NOBORDERS|255, (LPARAM)(LPTSTR)szString ); SendMessage( g_hwndStatusbar, SB_SIMPLE, 1, 0L ); UpdateWindow(g_hwndStatusbar); } /* ** Hopefully its one of ours */ else { if (flags & MF_SEPARATOR) { szString[0] = g_chNULL; } else { _tcscpy( szString, IdStr(item + MENU_STRING_BASE) ); } SendMessage( g_hwndStatusbar, SB_SETTEXT, SBT_NOBORDERS|255, (LPARAM)(LPTSTR)szString ); SendMessage( g_hwndStatusbar, SB_SIMPLE, 1, 0L ); UpdateWindow(g_hwndStatusbar); } } /*****************************Private*Routine******************************\ * CDPlay_OnDeviceChange * * * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ BOOL CDPlay_OnDeviceChange( HWND hwnd, WPARAM wParam, LPARAM lParam) { UINT uiEvent = (UINT)wParam; DWORD dwData = (DWORD)lParam; switch (uiEvent) { case DBT_DEVICEARRIVAL: // Insertion case DBT_DEVICEREMOVECOMPLETE: // Ejection if ((PDEV_BROADCAST_HDR)dwData) { switch (((PDEV_BROADCAST_HDR)dwData)->dbch_devicetype) { case DBT_DEVTYP_VOLUME: { TCHAR chDrive[4] = TEXT("A:\\"); INT i,j,drive; DWORD dwCurr; PDEV_BROADCAST_VOLUME pdbv; DWORD dwMask, dwDrives; pdbv = (PDEV_BROADCAST_VOLUME)dwData; dwMask = pdbv->dbcv_unitmask; dwDrives = GetLogicalDrives(); dwMask &= dwDrives; if (dwMask) { // Check all drives for match for (i = 0; i < 32; i++) { dwCurr = 1 << i; if (dwCurr & dwMask) { // Check drive chDrive[0] = TEXT('A') + i; if ( GetDriveType(chDrive) == DRIVE_CDROM ) { // Find Associated Drive structure drive = -1; for (j = 0; j < g_NumCdDevices; j++) { if (g_Devices[j]->drive == chDrive[0]) drive = j; } // Structure not found, make one if (drive == -1) { #ifdef DBG dprintf ("CDPlay_OnDeviceChange - didn't find drive"); #endif if (g_NumCdDevices > MAX_CD_DEVICES) { // Error - not enough device slots break; } g_Devices[g_NumCdDevices] = AllocMemory( sizeof(CDROM) ); if (NULL == g_Devices[g_NumCdDevices]) { // Error - unable to get enough memory break; } g_Devices[g_NumCdDevices]->drive = chDrive[0]; drive = g_NumCdDevices; g_NumCdDevices++; } // Insert/Eject new drive if (uiEvent == DBT_DEVICEARRIVAL) { // Drive has been inserted // Do nothing for an inserted CD-ROM // The Shell should inform us using // the AUTOPLAY through WM_COPYDDATA } else { // Drive has been ejected NoMediaUpdate (drive); } } } } } break; } default: // Not a logical volume message break; } } break; case DBT_DEVICEQUERYREMOVE: // Permission to remove a device is requested. case DBT_DEVICEQUERYREMOVEFAILED: // Request to remove a device has been canceled. case DBT_DEVICEREMOVEPENDING: // Device is about to be removed. Can not be denied. case DBT_DEVICETYPESPECIFIC: // Device-specific event. case DBT_CONFIGCHANGED: // Current configuration has changed. default: break; } return TRUE; } // End CDPlay_OnDeviceChange /*****************************Private*Routine******************************\ * CDPlay_OnDropFiles * * * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ void CDPlay_OnDropFiles( HWND hwnd, HDROP hdrop ) { int cFiles; int cGoodFiles; int iTextLen; int i; TCHAR szFileName[MAX_PATH+3]; LPTSTR lpCommandLine; BOOL fWasPlaying = FALSE; // Prevent Re-entrancy while we are // Opening and closing CD's if (g_fInCopyData) return; g_fInCopyData = TRUE; /* ** Determine how many files were passed to us. */ cFiles = DragQueryFile( hdrop, (UINT)-1, (LPTSTR)NULL, 0 ); /* ** Calculate the length of the command line each filename should be ** separated by a space character */ iTextLen = _tcslen( g_szCdplayer ); iTextLen += _tcslen( g_szPlayOption ); iTextLen += _tcslen( g_szTrackOption ); for ( cGoodFiles = cFiles, i = 0; i < cFiles; i++ ) { int unused1, unused2; DragQueryFile( hdrop, i, szFileName, MAX_PATH ); if (IsTrackFileNameValid( szFileName, &unused1, &unused2, TRUE, FALSE )) { // Add on 3 extra characters - one for the space and // two for quote marks, we do this because the filename // given may contain space characters. iTextLen += _tcslen( szFileName ) + 2 + 1; } else { cGoodFiles--; } } /* ** If the none of the dropped files are valid tracks just return */ if (cGoodFiles < 1) { g_fInCopyData = FALSE; return; } /* ** Allocate a chunk of memory big enough for all the options and ** filenames. Don't forget the NULL. */ lpCommandLine = AllocMemory(sizeof(TCHAR) * (iTextLen + 1)); /* ** Add a dummy intial command line arg. This is because the ** first arg is always the name of the invoked application. We ignore ** this paramter. Also if we are currently playing we need to ** add the -play option to command line as well as stop the CD. */ _tcscpy( lpCommandLine, g_szCdplayer ); if ( g_State & (CD_PLAYING | CD_PAUSED) ) { CdPlayerStopCmd(); fWasPlaying = TRUE; _tcscat( lpCommandLine, g_szPlayOption ); } /* ** If there is more than one file name specified then we should constuct ** a new playlist from the given files. */ if ( cGoodFiles > 1) { _tcscat( lpCommandLine, g_szTrackOption ); } /* ** Build up the command line. */ for ( i = 0; i < cFiles; i++ ) { int unused1, unused2; DragQueryFile( hdrop, i, szFileName, MAX_PATH ); if (IsTrackFileNameValid( szFileName, &unused1, &unused2, TRUE, TRUE )) { _tcscat( lpCommandLine, TEXT("\'") ); _tcscat( lpCommandLine, szFileName ); _tcscat( lpCommandLine, TEXT("\'") ); _tcscat( lpCommandLine, g_szBlank ); } } /* ** now process the newly constructed command line. */ HandlePassedCommandLine( lpCommandLine, FALSE ); /* ** If we were playing make sure that we are still playing the ** new track(s) */ if ( fWasPlaying ) { CdPlayerPlayCmd(); } LocalFree( lpCommandLine ); DragFinish( hdrop ); g_fInCopyData = FALSE; } /*****************************Private*Routine******************************\ * ResolveLink * * Takes the shortcut (shell link) file pointed to be szFileName and * resolves the link returning the linked file name in szFileName. * * szFileName must point to at least MAX_PATH amount of TCHARS. The function * return TRUE if the link was successfully resolved and FALSE otherwise. * * History: * dd-mm-94 - StephenE - Created * 03-11-95 - ShawnB - Unicode enabled * \**************************************************************************/ BOOL ResolveLink( TCHAR *szFileName ) { #ifndef UNICODE char cszPath[MAX_PATH]; WCHAR wszPath[MAX_PATH]; MultiByteToWideChar(CP_ACP, 0, szFileName, -1, wszPath, MAX_PATH); lstrcpy (cszPath, szFileName); #endif // End !UNICODE IShellLink * psl = NULL; HRESULT hres; int iCountTries; const int MAX_TRIES = 3; if (!g_fOleInitialized) { return FALSE; } // // Sometimes on Chicago I keep getting an error code from // CoCreateInstance that implies that the server is busy, // so I'll give it a chance to get its act together. // for ( iCountTries = 0; iCountTries < MAX_TRIES; iCountTries++ ) { hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC, &IID_IShellLink, (LPVOID *)&psl); if ( SUCCEEDED(hres) ) { break; } Sleep(100); } // // Make sure we that have the interface before proceeding // if (SUCCEEDED(hres) && psl != NULL) { IPersistFile *ppf; psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, (LPVOID *)&ppf); if ( NULL != ppf ) { #ifdef UNICODE hres = ppf->lpVtbl->Load(ppf, szFileName, 0); #else hres = ppf->lpVtbl->Load(ppf, wszPath, 0); #endif ppf->lpVtbl->Release(ppf); if ( FAILED(hres) ) { psl->lpVtbl->Release(psl); psl = NULL; return FALSE; } } else { psl->lpVtbl->Release(psl); psl = NULL; return FALSE; } } if ( NULL != psl ) { psl->lpVtbl->Resolve(psl, NULL, SLR_NO_UI); #ifdef UNICODE hres = psl->lpVtbl->GetPath(psl, szFileName, MAX_PATH, NULL, 0); #else hres = psl->lpVtbl->GetPath(psl, cszPath, MAX_PATH, NULL, 0); #endif psl->lpVtbl->Release(psl); #ifndef UNICODE if (SUCCEEDED(hres)) { lstrcpy (szFileName, cszPath); } #endif return SUCCEEDED(hres); } return FALSE; } //#endif /*****************************Private*Routine******************************\ * ShowStatusbar * * If the status bar is not visible: * Expand the client window, position the status bar and make it visible. * else * Hide the status bar, and then contract the client window. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void ShowStatusbar( void ) { RECT rcApp; GetWindowRect( g_hwndApp, &rcApp ); if (g_fStatusbarVisible) { ShowWindow( g_hwndStatusbar, SW_HIDE ); rcApp.bottom -= rcStatusbar.bottom; SetWindowPos( g_hwndApp, HWND_TOP, 0, 0, (int)(rcApp.right - rcApp.left), (int)(rcApp.bottom - rcApp.top), SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER ); } else { rcApp.bottom += rcStatusbar.bottom; SetWindowPos( g_hwndApp, HWND_TOP, 0, 0, (int)(rcApp.right - rcApp.left), (int)(rcApp.bottom - rcApp.top), SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER ); ShowWindow( g_hwndStatusbar, SW_SHOW ); } g_fStatusbarVisible = !g_fStatusbarVisible; } /*****************************Private*Routine******************************\ * ShowToolbar * * If the tool bar is not visible: * Grow the client window, position the child controls and show toolbar * else * Hide the tool bar, position controls and contract client window. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void ShowToolbar( void ) { RECT rcApp; HDWP hdwp; LONG lIncrement; int i; GetWindowRect( g_hwndApp, &rcApp ); if (g_fToolbarVisible) { lIncrement = -rcToolbar.bottom; ShowWindow( g_hwndToolbar, SW_HIDE ); } else { lIncrement = rcToolbar.bottom; } /* ** First resize the application. */ rcApp.bottom += lIncrement; SetWindowPos( g_hwndApp, HWND_TOP, 0, 0, (int)(rcApp.right - rcApp.left), (int)(rcApp.bottom - rcApp.top), SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER ); /* ** Now move the buttons and the track info stuff. */ hdwp = BeginDeferWindowPos( 20 ); for ( i = 0; i < NUM_OF_CONTROLS; i++ ) { rcControls[i].top += lIncrement; hdwp = DeferWindowPos( hdwp, g_hwndControls[i], HWND_TOP, (int)rcControls[i].left, (int)rcControls[i].top, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER ); } ASSERT(hdwp != NULL); EndDeferWindowPos( hdwp ); if (!g_fToolbarVisible) { ShowWindow( g_hwndToolbar, SW_SHOW ); } g_fToolbarVisible = !g_fToolbarVisible; } /*****************************Private*Routine******************************\ * ShowTrackInfo * * If the track info is not visible: * Expand the client window, position the track info and make it visible. * else * Hide the track info, and then contract the client window. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void ShowTrackInfo( void ) { RECT rcApp; int i; GetWindowRect( g_hwndApp, &rcApp ); if (g_fTrackInfoVisible) { for ( i = IDC_TRACKINFO_FIRST - IDC_CDPLAYER_FIRST; i < NUM_OF_CONTROLS; i++ ) { ShowWindow( g_hwndControls[i], SW_HIDE ); } rcApp.bottom -= cyTrackInfo; SetWindowPos( g_hwndApp, HWND_TOP, 0, 0, (int)(rcApp.right - rcApp.left), (int)(rcApp.bottom - rcApp.top), SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER ); } else { rcApp.bottom += cyTrackInfo; SetWindowPos( g_hwndApp, HWND_TOP, 0, 0, (int)(rcApp.right - rcApp.left), (int)(rcApp.bottom - rcApp.top), SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER ); for ( i = IDC_TRACKINFO_FIRST - IDC_CDPLAYER_FIRST; i < NUM_OF_CONTROLS; i++ ) { ShowWindow( g_hwndControls[i], SW_SHOW ); } } g_fTrackInfoVisible = !g_fTrackInfoVisible; } /******************************Public*Routine******************************\ * ChildEnumProc * * Gets the position of each child control window. As saves the associated * window handle for later use. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ BOOL CALLBACK ChildEnumProc( HWND hwndChild, LPARAM hwndParent ) { int index; index = INDEX(GetDlgCtrlID( hwndChild )); GetWindowRect( hwndChild, &rcControls[index] ); MapWindowPoints( NULL, (HWND)hwndParent, (LPPOINT)&rcControls[index], 2 ); g_hwndControls[index] = hwndChild; return TRUE; } /*****************************Private*Routine******************************\ * CreateToolbarAndStatusbar * * * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ BOOL CreateToolbarsAndStatusbar( HWND hwnd ) { HDC hdc; int RightOfPane[2]; RECT rcApp; HWND hwndToolbarTips; GetClientRect( hwnd, &rcApp ); cyTrackInfo = (rcApp.bottom - rcApp.top) - rcControls[IDC_TRACKINFO_FIRST - IDC_CDPLAYER_FIRST].top; g_hwndToolbar = CreateToolbarEx( hwnd, WS_CHILD | TBSTYLE_TOOLTIPS, ID_TOOLBAR, NUMBER_OF_BITMAPS, g_hInst, IDR_TOOLBAR, tbButtons, DEFAULT_TBAR_SIZE, dxBitmap, dyBitmap, dxBitmap, dyBitmap, sizeof(TBBUTTON) ); if ( g_hwndToolbar == NULL ) { return FALSE; } /* ** Calculate the size of the toolbar. The WM_SIZE message forces the ** toolbar to recalculate its size and thus return the correct value to ** us. */ SendMessage( g_hwndToolbar, WM_SIZE, 0, 0L ); GetClientRect( g_hwndToolbar, &rcToolbar ); hwndToolbarTips = (HWND)SendMessage( g_hwndToolbar, TB_GETTOOLTIPS, 0, 0L ); if (hwndToolbarTips) { g_lpfnToolTips = SubclassWindow( hwndToolbarTips, SubClassedToolTips ); } g_hwndStatusbar = CreateStatusWindow( WS_CHILD | WS_BORDER, g_szEmpty, hwnd, ID_STATUSBAR ); if ( g_hwndStatusbar == NULL ) { return FALSE; } /* ** Partition the status bar window into two. The first partion is ** 1.5 inches long. The other partition fills the remainder of ** the bar. */ hdc = GetWindowDC( hwnd ); #ifdef JAPAN //fix kksuzuka: #4131 RightOfPane[0] = (rcApp.right-rcApp.left) / 2; #else RightOfPane[0] = (GetDeviceCaps(hdc, LOGPIXELSX) * 3) / 2; #endif RightOfPane[1] = -1; ReleaseDC( hwnd, hdc ); SendMessage( g_hwndStatusbar, SB_SETPARTS, 2, (LPARAM)RightOfPane ); GetClientRect( g_hwndStatusbar, &rcStatusbar ); return TRUE; } /*****************************Private*Routine******************************\ * AdjustChildButtons * * The child buttons should be aligned with the right hand edge of the * track info controls. They should be be positioned so that vertically they * are in the centre of the space between the track info controls and * the top of the dialog box. * * The buttons are positioned such that the left hand edge of a button is * flush with the right hand edge of the next button. The play button is * 3 times the width of the other buttons. * * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void AdjustChildButtons( HWND hwnd ) { int cyVerticalSpace; int cxFromLeft; int i, x, y, cx, cy; SIZE sizeLed; HDC hdc; HFONT hFont; TCHAR szDisplay[16]; RECT rc; /* ** Calculate the max size that the display LED needs to be */ hdc = GetWindowDC( hwnd ); hFont = SelectObject( hdc, hLEDFontL ); wsprintf( szDisplay, TRACK_REM_FORMAT, 0, 0, TEXT("M"), 0 ); GetTextExtentPoint32( hdc, szDisplay, _tcslen(szDisplay), &sizeLed ); SelectObject( hdc, hFont ); ReleaseDC( hwnd, hdc ); /* ** Set the size of the CD Player window based on the max size of the LED display ** and the width of the buttons. */ GetWindowRect( hwnd, &rc ); SetWindowPos( hwnd, HWND_TOP, 0, 0, (4 * GetSystemMetrics(SM_CXBORDER)) + (5 * xFirstButton) + (5 * dxButton) + sizeLed.cx, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE ); /* ** Now work out hown much vertical space there is between the menu and the buttons ** and the buttons and the tack info display. */ cyVerticalSpace = (rcControls[INDEX(IDC_TRACKINFO_FIRST)].top - (2 * dyButton)) / 3; cxFromLeft = (4 * xFirstButton) + sizeLed.cx; /* ** There are 3 buttons on the top row. The first button is 3 times ** the width of the other buttons, it gets adjusted first. */ y = cyVerticalSpace; x = cxFromLeft; SetWindowPos( g_hwndControls[0], HWND_TOP, x, y, 3 * dxButton, dyButton, SWP_NOACTIVATE | SWP_NOZORDER ); x += (3 * dxButton); for ( i = 1; i < 3; i++ ) { SetWindowPos( g_hwndControls[i], HWND_TOP, x, y, dxButton, dyButton, SWP_NOACTIVATE | SWP_NOZORDER ); x += dxButton; } /* ** There are 5 buttons on the bottom row. */ y = dyButton + (2 * cyVerticalSpace); x = cxFromLeft; for ( i = 0; i < 5; i++ ) { SetWindowPos( g_hwndControls[i + 3], HWND_TOP, x, y, dxButton, dyButton, SWP_NOACTIVATE | SWP_NOZORDER ); x += dxButton; } /* ** Now adjust the LED window position. */ y = cyVerticalSpace; x = xFirstButton; /* see toolbar.h and toolbar.c for definition. */ cx = (2 * xFirstButton) + sizeLed.cx; cy = (2 * dyButton) + cyVerticalSpace; SetWindowPos( g_hwndControls[INDEX(IDC_LED)], HWND_TOP, x, y, cx, cy, SWP_NOACTIVATE | SWP_NOZORDER ); /* ** Now expand (or shrink) the track info controls to fit the available space. */ cx = (4 * xFirstButton) + (5 * dxButton) + sizeLed.cx; ComboBox_GetDroppedControlRect( g_hwndControls[INDEX(IDC_COMBO1)], &rc ); i = rc.bottom - rc.top; SetWindowPos( g_hwndControls[INDEX(IDC_COMBO1)], HWND_TOP, 0, 0, cx - rcControls[INDEX(IDC_COMBO1)].left, rcControls[INDEX(IDC_COMBO1)].bottom - rcControls[INDEX(IDC_COMBO1)].top + i, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE ); SetWindowPos( g_hwndControls[INDEX(IDC_EDIT1)], HWND_TOP, 0, 0, cx - rcControls[INDEX(IDC_EDIT1)].left, rcControls[INDEX(IDC_EDIT1)].bottom - rcControls[INDEX(IDC_EDIT1)].top, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE ); SetWindowPos( g_hwndControls[INDEX(IDC_COMBO2)], HWND_TOP, 0, 0, cx - rcControls[INDEX(IDC_COMBO2)].left, rcControls[INDEX(IDC_COMBO2)].bottom - rcControls[INDEX(IDC_COMBO2)].top + i, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE ); } /******************************Public*Routine******************************\ * FatalApplicationError * * Call this function if something "bad" happens to the application. It * displays an error message and then kills itself. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void FatalApplicationError( INT uIdStringResource, ... ) { va_list va; TCHAR chBuffer1[ STR_MAX_STRING_LEN ]; TCHAR chBuffer2[ STR_MAX_STRING_LEN ]; /* ** Load the relevant messages */ va_start(va, uIdStringResource); wvsprintf(chBuffer1, IdStr(uIdStringResource), va); va_end(va); _tcscpy( chBuffer2, IdStr(STR_FATAL_ERROR) ); /*"CD Player: Fatal Error"*/ /* ** How much of the application do we need to kill */ if (g_hwndApp) { if ( IsWindowVisible(g_hwndApp) ) { BringWindowToTop(g_hwndApp); } MessageBox( g_hwndApp, chBuffer1, chBuffer2, MB_ICONSTOP | MB_OK | MB_APPLMODAL | MB_SETFOREGROUND ); DestroyWindow( g_hwndApp ); } else { MessageBox( NULL, chBuffer1, chBuffer2, MB_APPLMODAL | MB_ICONSTOP | MB_OK | MB_SETFOREGROUND ); } ExitProcess( (UINT)-1 ); } /******************************Public*Routine******************************\ * IdStr * * Loads the given string resource ID into the passed storage. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ LPTSTR IdStr( int idResource ) { static TCHAR chBuffer[ STR_MAX_STRING_LEN ]; if (LoadString(g_hInst, idResource, chBuffer, STR_MAX_STRING_LEN) == 0) { return g_szEmpty; } return chBuffer; } /******************************Public*Routine******************************\ * CheckMenuItemIfTrue * * If "flag" TRUE the given menu item is checked, otherwise it is unchecked. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void CheckMenuItemIfTrue( HMENU hMenu, UINT idItem, BOOL flag ) { UINT uFlags; if (flag) { uFlags = MF_CHECKED | MF_BYCOMMAND; } else { uFlags = MF_UNCHECKED | MF_BYCOMMAND; } CheckMenuItem( hMenu, idItem, uFlags ); } /******************************Public*Routine******************************\ * ReadSettings * * Read app settings from ini file. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void ReadSettings( void ) { HKEY hKey; LONG lRet; /* ** See if the user has setting information stored in the registry. ** If so read the stuff from there. Otherwise fall thru and try to ** get the stuff from cdplayer.ini. */ lRet = RegOpenKey( HKEY_CURRENT_USER, g_szRegistryKey, &hKey ); if ( (lRet == ERROR_SUCCESS) ) { DWORD dwTmp, dwType, dwLen; int x, y; int cxApp, cyApp; int cxDesktop, cyDesktop; RECT rcApp, rcDesktop; // Save settings on exit ? dwLen = sizeof( g_fSaveOnExit ); if ( ERROR_SUCCESS != RegQueryValueEx( hKey, g_szSaveSettingsOnExit, 0L, &dwType, (LPBYTE)&g_fSaveOnExit, &dwLen ) ) { g_fSaveOnExit = TRUE; } // Small LED font dwLen = sizeof( g_fSmallLedFont ); RegQueryValueEx( hKey, g_szSmallFont, 0L, &dwType, (LPBYTE)&g_fSmallLedFont, &dwLen ); if (g_fSmallLedFont) { LED_ToggleDisplayFont(g_hwndControls[INDEX(IDC_LED)], g_fSmallLedFont ); } // Enable Tooltips dwLen = sizeof( g_fToolTips ); if ( ERROR_SUCCESS != RegQueryValueEx( hKey, g_szToolTips, 0L, &dwType, (LPBYTE)&g_fToolTips, &dwLen ) ) { g_fToolTips = TRUE; } /* ** Stop CD playing on exit ** ** As this is a new setting it might not present in systems ** that have upgraded. */ dwLen = sizeof( g_fStopCDOnExit ); if ( ERROR_SUCCESS != RegQueryValueEx(hKey, g_szStopCDPlayingOnExit, 0L, &dwType, (LPBYTE)&g_fStopCDOnExit, &dwLen) ) { g_fStopCDOnExit = TRUE; } // Play in selected order dwLen = sizeof( g_fSelectedOrder ); if ( ERROR_SUCCESS != RegQueryValueEx( hKey, g_szInOrderPlay, 0L, &dwType, (LPBYTE)&g_fSelectedOrder, &dwLen ) ) { g_fSelectedOrder = TRUE; } // Use single disk /* dwLen = sizeof( dwTmp ); if ( ERROR_SUCCESS != RegQueryValueEx( hKey, g_szMultiDiscPlay, 0L, &dwType, (LPBYTE)&dwTmp, &dwLen ) ) { dwTmp = FALSE; } g_fSingleDisk = !(BOOL)dwTmp; */ //if ( g_NumCdDevices < 2 ) { g_fMultiDiskAvailable = FALSE; g_fSingleDisk = TRUE; //} //else { // g_fMultiDiskAvailable = TRUE; //} // Current track time dwLen = sizeof( g_fDisplayT ); RegQueryValueEx( hKey, g_szDisplayT, 0L, &dwType, (LPBYTE)&g_fDisplayT, &dwLen ); // Time remaining for this track dwLen = sizeof( g_fDisplayTr ); RegQueryValueEx( hKey, g_szDisplayTr, 0L, &dwType, (LPBYTE)&g_fDisplayTr, &dwLen ); // Time remaining for this play list dwLen = sizeof( g_fDisplayDr ); RegQueryValueEx( hKey, g_szDisplayDr, 0L, &dwType, (LPBYTE)&g_fDisplayDr, &dwLen ); // Intro play (default 10Secs) dwLen = sizeof( g_fIntroPlay ); RegQueryValueEx( hKey, g_szIntroPlay, 0L, &dwType, (LPBYTE)&g_fIntroPlay, &dwLen ); dwLen = sizeof( g_IntroPlayLength ); RegQueryValueEx( hKey, g_szIntroPlayLen, 0L, &dwType, (LPBYTE)&g_IntroPlayLength, &dwLen ); /* ** As this is a new setting it might not present in systems ** that have upgraded. */ if (g_IntroPlayLength == 0) { g_IntroPlayLength = INTRO_DEFAULT_LEN; } // Continuous play (loop at end) dwLen = sizeof( g_fContinuous ); RegQueryValueEx( hKey, g_szContinuousPlay, 0L, &dwType, (LPBYTE)&g_fContinuous, &dwLen ); // Show toolbar dwLen = sizeof( g_fToolbarVisible ); RegQueryValueEx( hKey, g_szToolbar, 0L, &dwType, (LPBYTE)&g_fToolbarVisible, &dwLen ); // Show track information dwLen = sizeof( g_fTrackInfoVisible ); RegQueryValueEx( hKey, g_szDiscAndTrackDisplay, 0L, &dwType, (LPBYTE)&g_fTrackInfoVisible, &dwLen ); // Show track status bar dwLen = sizeof( g_fStatusbarVisible ); RegQueryValueEx( hKey, g_szStatusBar, 0L, &dwType, (LPBYTE)&g_fStatusbarVisible, &dwLen ); // X pos dwLen = sizeof( x ); RegQueryValueEx( hKey, g_szWindowOriginX, 0L, &dwType, (LPBYTE)&x, &dwLen ); // Y pos dwLen = sizeof( y ); RegQueryValueEx( hKey, g_szWindowOriginY, 0L, &dwType, (LPBYTE)&y, &dwLen ); GetClientRect( g_hwndApp, &rcApp ); GetWindowRect( GetDesktopWindow(), &rcDesktop ); cxDesktop = (rcDesktop.right - rcDesktop.left); cyDesktop = (rcDesktop.bottom - rcDesktop.top); cxApp = (rcApp.right - rcApp.left); cyApp = (rcApp.bottom - rcApp.top); if ( x < 0) { x = 0; } else if ( x > cxDesktop ) { x = (cxDesktop - cxApp); } if ( y < 0) { y = 0; } else if ( y > cyDesktop ) { y = (cyDesktop - cyApp); } SetWindowPos( g_hwndApp, HWND_TOP, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE ); /* ** Make sure that the LED display format is correct */ if ( g_fDisplayT == FALSE && g_fDisplayTr == FALSE && g_fDisplayDr == FALSE ) { g_fDisplayT = TRUE; } RegCloseKey( hKey ); } else { RECT r; TCHAR s[80],t[80]; int i, j; if (lRet == ERROR_SUCCESS) { // presumably dwDesposition == REG_CREATED_NEW_KEY RegCloseKey( hKey ); } g_fSmallLedFont = GetPrivateProfileInt( g_szSettings, g_szSmallFont, FALSE, g_IniFileName ); if (g_fSmallLedFont) { LED_ToggleDisplayFont(g_hwndControls[INDEX(IDC_LED)], g_fSmallLedFont ); } g_fToolTips = GetPrivateProfileInt( g_szSettings, g_szToolTips, TRUE, g_IniFileName ); /* ** Get disc play settings */ i = GetPrivateProfileInt( g_szSettings, g_szInOrderPlay, FALSE, g_IniFileName ); j = GetPrivateProfileInt( g_szSettings, g_szRandomPlay, FALSE, g_IniFileName ); /* ** Because the orignal CD Player had a silly way of recording ** whether the state was random or inorder play we need the following ** state table. ** ** if i == j => g_fSelectedOrder = TRUE; ** else => g_fSelectedOrder = i; */ if ( i == j ) { g_fSelectedOrder = TRUE; } else { g_fSelectedOrder = (BOOL)i; } //i = GetPrivateProfileInt( g_szSettings, g_szMultiDiscPlay, // 3, g_IniFileName ); //if (i == 0 || i == 3) { // g_fSingleDisk = TRUE; //} //else { // g_fSingleDisk = FALSE; //} //if ( g_NumCdDevices < 2 ) { g_fMultiDiskAvailable = FALSE; g_fSingleDisk = TRUE; //} //else { /// g_fMultiDiskAvailable = TRUE; //} g_fIntroPlay = (BOOL)GetPrivateProfileInt( g_szSettings, g_szIntroPlay, FALSE, g_IniFileName ); g_IntroPlayLength = (BOOL)GetPrivateProfileInt( g_szSettings, g_szIntroPlayLen, INTRO_DEFAULT_LEN, g_IniFileName ); g_fContinuous = (BOOL)GetPrivateProfileInt( g_szSettings, g_szContinuousPlay, FALSE, g_IniFileName ); g_fSaveOnExit = (BOOL)GetPrivateProfileInt( g_szSettings, g_szSaveSettingsOnExit, TRUE, g_IniFileName ); g_fStopCDOnExit = (BOOL)GetPrivateProfileInt( g_szSettings, g_szStopCDPlayingOnExit, TRUE, g_IniFileName ); g_fDisplayT = (BOOL)GetPrivateProfileInt( g_szSettings, g_szDisplayT, TRUE, g_IniFileName ); g_fDisplayTr = (BOOL)GetPrivateProfileInt( g_szSettings, g_szDisplayTr, FALSE, g_IniFileName ); g_fDisplayDr = (BOOL)GetPrivateProfileInt( g_szSettings, g_szDisplayDr, FALSE, g_IniFileName ); /* ** When the app is created the toolbar is inially NOT shown. Therefore ** only show it if the user requests it. */ g_fToolbarVisible = (BOOL)GetPrivateProfileInt( g_szSettings, g_szToolbar, FALSE, g_IniFileName ); /* ** When the app is created the track info stuff is initially shown. ** Therefore only hide it if the user requests it. */ g_fTrackInfoVisible = (BOOL)GetPrivateProfileInt( g_szSettings, g_szDiscAndTrackDisplay, TRUE, g_IniFileName ); /* ** When the app is created the statusbar is initially NOT shown. ** Therefore only show it if the user requests it. */ g_fStatusbarVisible = (BOOL)GetPrivateProfileInt( g_szSettings, g_szStatusBar, TRUE, g_IniFileName ); GetWindowRect( g_hwndApp, &r ); wsprintf(t, TEXT("%d %d"),r.left, r.top); GetPrivateProfileString( g_szSettings, g_szWindowOrigin, t, s, 80, g_IniFileName ); _stscanf(s, TEXT("%d %d"), &r.left, &r.top); SetWindowPos( g_hwndApp, HWND_TOP, r.left, r.top, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE ); } /* ** Update the various buttons, toolbars and statusbar */ if (g_fToolbarVisible) { g_fToolbarVisible = FALSE; ShowToolbar(); } if (!g_fTrackInfoVisible) { g_fTrackInfoVisible = TRUE; ShowTrackInfo(); } if (g_fStatusbarVisible) { g_fStatusbarVisible = FALSE; ShowStatusbar(); } /* ** If there is only one disc drive available remove the "Multidisc play" ** menu item. UpdateToolbarButtons below will take care of the ** Multidisc toolbar button. */ //if ( !g_fMultiDiskAvailable ) { { HMENU hMenu = GetSubMenu( GetMenu(g_hwndApp), 2 ); DeleteMenu( hMenu, IDM_OPTIONS_MULTI, MF_BYCOMMAND ); } UpdateToolbarButtons(); UpdateToolbarTimeButtons(); } /******************************Public*Routine******************************\ * UpdateToolbarButtons * * Ensures that the toolbar buttons are in the correct state. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void UpdateToolbarButtons( void ) { LRESULT lResult; /* ** Read the current state of the button. If the current button state ** does not match (!g_fSelectedOrder) we need to toggle the button state. */ lResult = SendMessage( g_hwndToolbar, TB_ISBUTTONCHECKED, IDM_OPTIONS_RANDOM, 0L ); if ( g_fSelectedOrder || (lResult == 0L) ) { SendMessage( g_hwndToolbar, TB_CHECKBUTTON, IDM_OPTIONS_RANDOM, (LPARAM)!g_fSelectedOrder ); } /* ** Whats the current visible state of the multi-disk button ? ** If lResult != 0 the button is already visible. */ lResult = SendMessage( g_hwndToolbar, TB_ISBUTTONHIDDEN, IDM_OPTIONS_MULTI, 0L ); /* ** Does the multi-disk button visible state match the g_fMultiDiskAvailable ** flag ? If so we need to toggle the buttons visible state. */ //if ( !(g_fMultiDiskAvailable && lResult) ) { SendMessage( g_hwndToolbar, TB_HIDEBUTTON, IDM_OPTIONS_MULTI, MAKELPARAM( !g_fMultiDiskAvailable, 0) ); //} /* ** If there are multiple discs available does the current ** state of the MULTI disc button match the state of (!g_fSingleDisk) */ if (g_fMultiDiskAvailable) { lResult = SendMessage( g_hwndToolbar, TB_ISBUTTONCHECKED, IDM_OPTIONS_MULTI, 0L ); if ( g_fSingleDisk || (lResult == 0L) ) { SendMessage( g_hwndToolbar, TB_CHECKBUTTON, IDM_OPTIONS_MULTI, (LPARAM)!g_fSingleDisk ); } } /* ** Read the current state of the button. If the current button state ** does not match g_fContinuous we need to toggle the button state. */ lResult = SendMessage( g_hwndToolbar, TB_ISBUTTONCHECKED, IDM_OPTIONS_CONTINUOUS, (LPARAM)0L ); if ( !(g_fContinuous && lResult) ) { SendMessage( g_hwndToolbar, TB_CHECKBUTTON, IDM_OPTIONS_CONTINUOUS, (LPARAM)g_fContinuous ); } /* ** Read the current state of the button. If the current button state ** does not match g_fIntroPlay we need to toggle the button state. */ lResult = SendMessage( g_hwndToolbar, TB_ISBUTTONCHECKED, IDM_OPTIONS_INTRO, (LPARAM)0L ); if ( !(g_fIntroPlay && lResult) ) { SendMessage( g_hwndToolbar, TB_CHECKBUTTON, IDM_OPTIONS_INTRO, (LPARAM)g_fIntroPlay ); } /* ** Turn the tool tips on or off */ EnableToolTips( g_fToolTips ); } /******************************Public*Routine******************************\ * UpdateToolbarTimeButtons * * Ensures that the time remaining toolbar buttons are in the correct state. * This function should only be called from the LED wndproc when it receives * a mouse button up message. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void UpdateToolbarTimeButtons( void ) { if (g_fDisplayT) { SendMessage( g_hwndToolbar, TB_CHECKBUTTON, IDM_TIME_REMAINING, (LPARAM)g_fDisplayT ); } else if (g_fDisplayTr) { SendMessage( g_hwndToolbar, TB_CHECKBUTTON, IDM_TRACK_REMAINING, (LPARAM)g_fDisplayTr ); } else if (g_fDisplayDr) { SendMessage( g_hwndToolbar, TB_CHECKBUTTON, IDM_DISC_REMAINING, (LPARAM)g_fDisplayDr ); } } /******************************Public*Routine******************************\ * LockTableOfContents * * This function is used to determine if it is valid for the UI thread * to access the table of contents for the specified CD Rom. If this * function returns FALSE the UI thread should NOT touch the table of * contents for this CD Rom. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ BOOL LockTableOfContents( int cdrom ) { DWORD dwRet; if (g_Devices[cdrom]->fIsTocValid) { return TRUE; } if (g_Devices[cdrom]->hThreadToc == NULL) { return FALSE; } dwRet = WaitForSingleObject(g_Devices[cdrom]->hThreadToc, 0L ); if (dwRet == WAIT_OBJECT_0) { GetExitCodeThread( g_Devices[cdrom]->hThreadToc, &dwRet ); g_Devices[cdrom]->fIsTocValid = (BOOL)dwRet; CloseHandle( g_Devices[cdrom]->hThreadToc ); g_Devices[cdrom]->hThreadToc = NULL; } return g_Devices[cdrom]->fIsTocValid; } /******************************Public*Routine******************************\ * LockAllTableOfContents * * This function is used to determine if it is valid for the UI thread * to access the table of contents for the ALL the cdroms devices. * The function returns FALSE the UI thread should NOT touch the table of * contents for any CD Rom. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ BOOL LockALLTableOfContents( void ) { BOOL fLock; int i; for (i = 0, fLock = TRUE; fLock && (i < g_NumCdDevices); i++) { fLock = LockTableOfContents(i); } return fLock; } /******************************Public*Routine******************************\ * AllocMemory * * Allocates a memory of the given size. This function will terminate the * application if the allocation failed. Memory allocated by this function * must be freed with LocalFree. The memory should not be locked or unlocked. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ LPVOID AllocMemory( UINT uSize ) { LPVOID lp; lp = LocalAlloc( LPTR, uSize ); if (lp == NULL) { /* ** No memory - no application, simple ! */ FatalApplicationError( STR_FAIL_INIT ); } return lp; } /******************************Public*Routine******************************\ * SetPlayButtonsEnableState * * Sets the play buttons enable state to match the state of the current * cdrom device. See below... * * * CDPlayer buttons enable state table * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÂÄÄÄÄÄÄ¿ * ³E=Enabled D=Disabled ³ Play ³ Pause ³ Eject ³ Stop ³ Other ³DataB ³ * ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄ´ * ³Disk in use ³ D ³ D ³ D ³ D ³ D ³ D ³ * ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄ´ * ³No music cd or data cdrom ³ D ³ D ³ E ³ D ³ D ³ D ³ * ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄ´ * ³Music cd (playing) ³ D ³ E ³ E ³ E ³ E ³ E ³ * ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄ´ * ³Music cd (paused) ³ E ³ E ³ E ³ E ³ E ³ E ³ * ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄ´ * ³Music cd (stopped) ³ E ³ D ³ E ³ D ³ E ³ E ³ * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÙ * * Note that the DataB(ase) button is actually on the toolbar. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void SetPlayButtonsEnableState( void ) { BOOL fEnable; BOOL fMusicCdLoaded; BOOL fActivePlayList; int i; /* ** Do we have a music cd loaded. */ if (g_State & (CD_BEING_SCANNED | CD_NO_CD | CD_DATA_CD_LOADED | CD_IN_USE)) { fMusicCdLoaded = FALSE; } else { fMusicCdLoaded = TRUE; } /* ** Is there an active playlist */ if ( (CDTIME(g_CurrCdrom).TotalMin == 0) && (CDTIME(g_CurrCdrom).TotalSec == 0) ) { fActivePlayList = FALSE; } else { fActivePlayList = TRUE; } /* ** Do the play button */ if ( fMusicCdLoaded && fActivePlayList && ((g_State & CD_STOPPED) || (g_State & CD_PAUSED))) { fEnable = TRUE; } else { fEnable = FALSE; } EnableWindow( g_hwndControls[INDEX(IDM_PLAYBAR_PLAY)], fEnable ); /* ** Do the stop and pause buttons */ if ( fMusicCdLoaded && fActivePlayList && ((g_State & CD_PLAYING) || (g_State & CD_PAUSED))) { EnableWindow( g_hwndControls[INDEX(IDM_PLAYBAR_STOP)], TRUE ); EnableWindow( g_hwndControls[INDEX(IDM_PLAYBAR_PAUSE)], TRUE ); } else { EnableWindow( g_hwndControls[INDEX(IDM_PLAYBAR_STOP)], FALSE ); EnableWindow( g_hwndControls[INDEX(IDM_PLAYBAR_PAUSE)], FALSE ); } /* ** Do the remaining buttons */ for ( i = IDM_PLAYBAR_PREVTRACK; i <= IDM_PLAYBAR_NEXTTRACK; i++ ) { EnableWindow( g_hwndControls[INDEX(i)], (fMusicCdLoaded && fActivePlayList) ); } /* ** If the drive is in use then we must diable the eject button. */ EnableWindow( g_hwndControls[INDEX(IDM_PLAYBAR_EJECT)], (g_State & (CD_BEING_SCANNED | CD_IN_USE)) ? FALSE : TRUE ); /* ** Now do the database button on the toolbar. */ SendMessage( g_hwndToolbar, TB_ENABLEBUTTON, IDM_DATABASE_EDIT, (LPARAM)fMusicCdLoaded ); /* ** Make sure that the keyboard focus is on the Eject button ** if we have not got a CD loaded. */ if ( GetFocus() == NULL ) { if ( g_State & (CD_NO_CD | CD_DATA_CD_LOADED) ) { SetFocus( g_hwndControls[INDEX(IDM_PLAYBAR_EJECT)] ); } } } /******************************Public*Routine******************************\ * HeartBeatTimerProc * * This function is responsible for. * * 1. detecting new or ejected cdroms. * 2. flashing the LED display if we are in paused mode. * 3. Incrementing the LED display if we are in play mode. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void CALLBACK HeartBeatTimerProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime ) { static DWORD dwTickCount; int i; DWORD dwMod; ++dwTickCount; dwMod = (dwTickCount % 6); /* ** Check for new/ejected cdroms, do this every three seconds. */ #if 0 // Note: This code no longer necessary, it is done on the // WM_DEVICECHANGE message instead!!! if ( 0 == dwMod ) { for (i = 0; i < g_NumCdDevices; i++) { if ( (!(g_Devices[i]->State & CD_EDITING)) && (!(g_Devices[i]->State & CD_PLAYING)) ) { CheckUnitCdrom(i, FALSE); } } } #endif if ( g_State & CD_PLAYING ) { if ( LockALLTableOfContents() ) { SyncDisplay(); } } /* ** If we are paused and NOT skipping flash the display. */ else if ((g_State & CD_PAUSED) && !(g_State & CD_SEEKING)) { HWND hwnd; switch ( dwMod ) { case 2: case 5: case 8: case 11: if ( g_fIsIconic ) { SetWindowText( g_hwndApp, g_szBlank ); UpdateWindow( g_hwndApp ); } else { hwnd = g_hwndControls[INDEX(IDC_LED)]; g_fFlashLed = TRUE; SetWindowText( hwnd, g_szBlank ); g_fFlashLed = FALSE; } break; case 0: case 3: case 6: case 9: UpdateDisplay( DISPLAY_UPD_LED ); break; } } } /******************************Public*Routine******************************\ * SkipBeatTimerProc * * This function is responsible for advancing or retreating the current * playing position. * * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void CALLBACK SkipBeatTimerProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime ) { /* ** Deteremine if it is time to accelerate the skipping frequency. */ switch (++g_AcceleratorCount) { case SKIP_ACCELERATOR_LIMIT1: KillTimer( hwnd, idEvent ); SetTimer( hwnd, idEvent, SKIPBEAT_TIMER_RATE2, SkipBeatTimerProc ); break; case SKIP_ACCELERATOR_LIMIT2: KillTimer( hwnd, idEvent ); SetTimer( hwnd, idEvent, SKIPBEAT_TIMER_RATE3, SkipBeatTimerProc ); break; } if ( LockALLTableOfContents() ) { if ( idEvent == IDM_PLAYBAR_SKIPFORE) { TimeAdjustIncSecond( g_CurrCdrom ); /* ** When TimeAjustIncSecond gets to the end of the last track ** it sets CURRTRACK(g_CurrCdrom) equal to NULL. When this ** occurs we effectively reset the CD Player */ if ( CURRTRACK(g_CurrCdrom) == NULL ) { if ( g_State & (CD_WAS_PLAYING | CD_PAUSED) ) { SendMessage( g_hwndControls[INDEX(IDM_PLAYBAR_STOP)], WM_LBUTTONDOWN, 0, 0L ); SendMessage( g_hwndControls[INDEX(IDM_PLAYBAR_STOP)], WM_LBUTTONUP, 0, 0L ); } else { /* ** Seek to the first playable track. */ CURRTRACK(g_CurrCdrom) = FindFirstTrack( g_CurrCdrom ); if ( CURRTRACK(g_CurrCdrom) != NULL ) { TimeAdjustSkipToTrack( g_CurrCdrom, CURRTRACK(g_CurrCdrom) ); UpdateDisplay( DISPLAY_UPD_LED | DISPLAY_UPD_TRACK_TIME | DISPLAY_UPD_TRACK_NAME ); SetPlayButtonsEnableState(); SetFocus( g_hwndControls[INDEX(IDM_PLAYBAR_PLAY)] ); } } } } else { TimeAdjustDecSecond( g_CurrCdrom ); } } } /******************************Public*Routine******************************\ * UpdateDisplay * * This routine updates the display according to the flags that * are passed in. The display consists of the LED display, the * track and title names, the disc and track lengths and the cdrom * combo-box. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void UpdateDisplay( DWORD Flags ) { TCHAR lpsz[55]; TCHAR lpszIcon[75]; PTRACK_PLAY tr; int track; int mtemp, stemp, m, s; /* ** Check for valid flags */ if ( Flags == 0 ) { return; } /* ** Grab current track information */ if (CURRTRACK(g_CurrCdrom) != NULL) { track = CURRTRACK(g_CurrCdrom)->TocIndex + FIRSTTRACK(g_CurrCdrom); } else { track = 0; } /* ** Update the LED box? */ if (Flags & DISPLAY_UPD_LED) { /* ** Update LED box */ if (g_fDisplayT) { if (Flags & DISPLAY_UPD_LEADOUT_TIME) { wsprintf( lpsz, TRACK_TIME_LEADOUT_FORMAT, track, CDTIME(g_CurrCdrom).TrackCurMin, g_szTimeSep, CDTIME(g_CurrCdrom).TrackCurSec ); } else { wsprintf( lpsz, TRACK_TIME_FORMAT, track, CDTIME(g_CurrCdrom).TrackCurMin, g_szTimeSep, CDTIME(g_CurrCdrom).TrackCurSec ); } } if (g_fDisplayTr) { if (Flags & DISPLAY_UPD_LEADOUT_TIME) { wsprintf( lpsz, TRACK_REM_FORMAT, track - 1, CDTIME(g_CurrCdrom).TrackCurMin, g_szTimeSep, CDTIME(g_CurrCdrom).TrackCurSec ); } else { wsprintf( lpsz, TRACK_REM_FORMAT, track, CDTIME(g_CurrCdrom).TrackRemMin, g_szTimeSep, CDTIME(g_CurrCdrom).TrackRemSec ); } } if (g_fDisplayDr) { /* ** Compute remaining time */ mtemp = stemp = m = s =0; if (CURRTRACK(g_CurrCdrom) != NULL) { for ( tr = CURRTRACK(g_CurrCdrom)->nextplay; tr != NULL; tr = tr->nextplay ) { FigureTrackTime( g_CurrCdrom, tr->TocIndex, &mtemp, &stemp ); m+=mtemp; s+=stemp; } m+= CDTIME(g_CurrCdrom).TrackRemMin; s+= CDTIME(g_CurrCdrom).TrackRemSec; } m+= (s / 60); s = (s % 60); CDTIME(g_CurrCdrom).RemMin = m; CDTIME(g_CurrCdrom).RemSec = s; wsprintf( lpsz, DISC_REM_FORMAT, CDTIME(g_CurrCdrom).RemMin, g_szTimeSep, CDTIME(g_CurrCdrom).RemSec ); } SetWindowText( g_hwndControls[INDEX(IDC_LED)], lpsz ); if (g_fIsIconic) { wsprintf( lpszIcon, IdStr( STR_CDPLAYER_TIME ), lpsz ); SetWindowText( g_hwndApp, lpszIcon ); } } /* ** Update Title? */ if (Flags & DISPLAY_UPD_TITLE_NAME) { ComputeDriveComboBox( ); SetWindowText( g_hwndControls[INDEX(IDC_TITLE_NAME)], (LPCTSTR)TITLE(g_CurrCdrom) ); } /* ** Update track name? */ if (Flags & DISPLAY_UPD_TRACK_NAME) { HWND hwnd; hwnd = g_hwndControls[INDEX(IDC_TRACK_LIST)]; if (CURRTRACK(g_CurrCdrom) != NULL) { track = 0; for( tr = PLAYLIST(g_CurrCdrom); tr != CURRTRACK(g_CurrCdrom); tr = tr->nextplay, track++ ); ComboBox_SetCurSel( hwnd, track ); } else { ComboBox_SetCurSel( hwnd, 0 ); } } /* ** Update disc time? */ if (Flags & DISPLAY_UPD_DISC_TIME) { wsprintf( lpsz, IdStr( STR_TOTAL_PLAY ), /*"Total Play: %02d:%02d m:s", */ CDTIME(g_CurrCdrom).TotalMin, g_szTimeSep, CDTIME(g_CurrCdrom).TotalSec, g_szTimeSep ); SendMessage( g_hwndStatusbar, SB_SETTEXT, 0, (LPARAM)lpsz ); } /* ** Update track time? */ if (Flags & DISPLAY_UPD_TRACK_TIME) { wsprintf( lpsz, IdStr( STR_TRACK_PLAY ), /* "Track: %02d:%02d m:s", */ CDTIME(g_CurrCdrom).TrackTotalMin, g_szTimeSep, CDTIME(g_CurrCdrom).TrackTotalSec, g_szTimeSep ); SendMessage( g_hwndStatusbar, SB_SETTEXT, 1, (LPARAM)lpsz ); } } /******************************Public*Routine******************************\ * Common_OnCtlColor * * Here we return a brush to paint the background with. The brush is the same * color as the face of a button. We also set the text background color so * that static controls draw correctly. This function is shared with the * disk info/editing dialog box. * * History: * dd-mm-93 - StephenE - Created * \**************************************************************************/ HBRUSH Common_OnCtlColor( HWND hwnd, HDC hdc, HWND hwndChild, int type ) { SetBkColor( hdc, rgbFace ); return g_hBrushBkgd; } /******************************Public*Routine******************************\ * Common_OnMeasureItem * * All items are the same height and width. * * We only have to update the height field for owner draw combo boxes and * list boxes. This function is shared with the disk edit/info dialog box. * * History: * dd-mm-93 - StephenE - Created * \**************************************************************************/ BOOL Common_OnMeasureItem( HWND hwnd, MEASUREITEMSTRUCT *lpMeasureItem ) { HFONT hFont; int cyBorder; LOGFONT lf; hFont = GetWindowFont( hwnd ); #ifdef JAPAN //fix kksuzuka: #3116 //in 16, 20, 24 dot font, some charcters, such as "g" or "p", needs more rooms. { UINT iHeight; if ( hFont != NULL ) { GetObject( hFont, sizeof(lf), &lf ); } else { SystemParametersInfo( SPI_GETICONTITLELOGFONT, sizeof(lf), (LPVOID)&lf, 0 ); } iHeight = ABS( lf.lfHeight ); lpMeasureItem->itemHeight = iHeight / 2 * 3; } #else if ( hFont != NULL ) { GetObject( hFont, sizeof(lf), &lf ); cyBorder = GetSystemMetrics( SM_CYBORDER ); lpMeasureItem->itemHeight = ABS( lf.lfHeight ) + (4 * cyBorder); } else { lpMeasureItem->itemHeight = 14; } #endif return TRUE; } /******************************Public*Routine******************************\ * DrawTrackItem * * This routine draws the information in a cell of the track name * combo box. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void DrawTrackItem( HDC hdc, const RECT *r, DWORD item, BOOL selected ) { SIZE si; int i; int cxTrk; PTRACK_INF t; TCHAR s[ARTIST_LENGTH]; TCHAR szTrk[16]; /* ** Check for invalid items */ if ( item == (DWORD)-1 ) { return; } if ( ALLTRACKS(g_CurrCdrom) == NULL ) { return; } /* ** Check selection status, and set up to draw correctly */ if ( selected ) { SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) ); SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) ); } else { SetBkColor( hdc, GetSysColor(COLOR_WINDOW)); SetTextColor( hdc, GetSysColor(COLOR_WINDOWTEXT)); } /* ** Get track info */ t = FindTrackNodeFromTocIndex( item, ALLTRACKS( g_CurrCdrom ) ); if ( (t != NULL) && (t->name != NULL) ) { /* ** Do we need to munge track name (clip to listbox)? */ wsprintf(szTrk, TEXT("<%02d> "), t->TocIndex + FIRSTTRACK(g_CurrCdrom)); GetTextExtentPoint( hdc, szTrk, _tcslen(szTrk), &si ); cxTrk = si.cx; i = _tcslen( t->name ) + 1; do { GetTextExtentPoint( hdc, t->name, --i, &si ); } while( si.cx > (r->right - r->left - cxTrk) ); ZeroMemory( s, TRACK_TITLE_LENGTH * sizeof( TCHAR ) ); _tcsncpy( s, t->name, i ); } else { _tcscpy( s, g_szBlank ); i = 1; } /* ** Draw track name */ ExtTextOut( hdc, r->left, r->top, ETO_OPAQUE | ETO_CLIPPED, r, s, i, NULL ); /* ** draw track number */ if ( t != NULL ) { ExtTextOut( hdc, r->right - cxTrk, r->top, ETO_CLIPPED, r, szTrk, _tcslen( szTrk ), NULL ); } } /******************************Public*Routine******************************\ * DrawDriveItem * * This routine draws the information in a cell of the drive/artist * combo box. * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void DrawDriveItem( HDC hdc, const RECT *r, DWORD item, BOOL selected ) { SIZE si; int i; int j; int cxDrv; TCHAR szDrv[16]; /* ** Check for invalid items */ if ( item == (DWORD)-1 ) { return; } /* ** Check selection status, and set up to draw correctly */ if (selected) { SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) ); SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) ); } else { SetBkColor( hdc, GetSysColor(COLOR_WINDOW)); SetTextColor( hdc, GetSysColor(COLOR_WINDOWTEXT)); } /* ** Do we need to munge artist name (clip)? */ wsprintf( szDrv, TEXT("<%c:> "), g_Devices[item]->drive ); j = _tcslen( szDrv ); GetTextExtentPoint( hdc, szDrv, j, &si ); cxDrv = si.cx; i = _tcslen( ARTIST(item) ) + 1; do { GetTextExtentPoint( hdc, ARTIST(item), --i, &si ); } while( si.cx > (r->right - r->left - cxDrv) ); /* ** Draw artist name */ ExtTextOut( hdc, r->left, r->top, ETO_OPAQUE | ETO_CLIPPED, r, ARTIST(item), i, NULL ); /* ** draw drive letter */ ExtTextOut( hdc, r->right - cxDrv, r->top, ETO_CLIPPED, r, szDrv, j, NULL ); } /*****************************Private*Routine******************************\ * StartSndVol * * Trys to start sndvol (the NT sound volume piglet). * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ void StartSndVol( DWORD unused ) { /* ** WinExec returns a value greater than 31 if suceeds */ g_fVolumeController = (WinExec( "sndvol32", SW_SHOW ) > 31); ExitThread( 0 ); } /*****************************Private*Routine******************************\ * EnableToolTips * * Enables or disables the display of tool tips according to the fEnable * parameter * * History: * 18-09-94 - StephenE - Created * \**************************************************************************/ void EnableToolTips( BOOL fEnable ) { HWND hwndTips; hwndTips = (HWND)SendMessage( g_hwndApp, TB_GETTOOLTIPS, 0, 0L ); SendMessage( hwndTips, TTM_ACTIVATE, fEnable, 0L ); hwndTips = (HWND)SendMessage( g_hwndToolbar, TB_GETTOOLTIPS, 0, 0L ); SendMessage( hwndTips, TTM_ACTIVATE, fEnable, 0L ); } /*****************************Private*Routine******************************\ * HandlePassedCommandLine * * This function gets called to handle command line options that are passed to * CDPlayer as the result of the WM_DROPFILES or WM_COPYDATA messages. * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ void HandlePassedCommandLine( LPTSTR lpCmdLine, BOOL fCheckCDRom ) { int i; int iTrack = -1, iCDrom; iCDrom = ParseCommandLine( lpCmdLine, &iTrack, TRUE ); if ((iCDrom < 0) || (iCDrom >= g_NumCdDevices)) return; // CheckUnitCDRom to reload Table of Contents if ( fCheckCDRom ) { CheckUnitCdrom(iCDrom, TRUE); while( !LockTableOfContents(iCDrom) ) { MSG msg; GetMessage( &msg, NULL, WM_NOTIFY_TOC_READ, WM_NOTIFY_TOC_READ ); DispatchMessage( &msg ); } } if (iCDrom != g_CurrCdrom) { HWND hwndBtn = g_hwndControls[INDEX(IDC_ARTIST_NAME)]; SwitchToCdrom( iCDrom, TRUE ); SetPlayButtonsEnableState(); SendMessage( hwndBtn, CB_SETCURSEL, (WPARAM)iCDrom, 0 ); } /* ** Initialize the new play list for each drive. */ for ( i = 0; i < g_NumCdDevices; i++) { TimeAdjustInitialize( i ); } // Set Current Track to specified track if ( iTrack != -1 ) { PTRACK_PLAY tr; tr = PLAYLIST( g_CurrCdrom ); if ( tr != NULL ) { for( i = 0; i < iTrack; i++, tr = tr->nextplay ); TimeAdjustSkipToTrack( g_CurrCdrom, tr ); } } } /******************************Public*Routine******************************\ * IsPlayOptionGiven * * Checks the command line to see if the "-play" option has been passed. * * History: * dd-mm-95 - StephenE - Created * \**************************************************************************/ BOOL IsPlayOptionGiven( LPTSTR lpstr ) { TCHAR chOption[MAX_PATH]; /* ** Start by converting everything to upper case. */ CharUpperBuff( lpstr, _tcslen(lpstr) ); /* ** The first parameter on the command line is always the ** string used invoke the program. ie cdplayer.exe */ lpstr += _tcsspn( lpstr, g_szBlank ); lpstr += CopyWord( chOption, lpstr ); /* ** Remove leading space */ lpstr += _tcsspn( lpstr, g_szBlank ); /* ** process any command line options */ while ( (*lpstr == g_chOptionHyphen) || (*lpstr == g_chOptionSlash) ) { /* ** pass over the option delimeter */ lpstr++; /* ** Copy option and skip over it. */ lpstr += CopyWord( chOption, lpstr ); /* ** Is this the play option ?? If so, don't bother processing anymore ** options. */ if ( 0 == _tcscmp( chOption, g_szPlay ) ) { return TRUE; } /* ** Remove leading space */ lpstr += _tcsspn( lpstr, g_szBlank ); } return FALSE; } /******************************Public*Routine******************************\ * IsUpdateOptionGiven * * Checks the command line to see if the "-update" option has been passed. * * History: * dd-mm-95 - StephenE - Created * \**************************************************************************/ BOOL IsUpdateOptionGiven( LPTSTR lpstr ) { TCHAR chOption[MAX_PATH]; /* ** Start by converting everything to upper case. */ CharUpperBuff( lpstr, _tcslen(lpstr) ); /* ** The first parameter on the command line is always the ** string used invoke the program. ie cdplayer.exe */ lpstr += _tcsspn( lpstr, g_szBlank ); lpstr += CopyWord( chOption, lpstr ); /* ** Remove leading space */ lpstr += _tcsspn( lpstr, g_szBlank ); /* ** process any command line options */ while ( (*lpstr == g_chOptionHyphen) || (*lpstr == g_chOptionSlash) ) { /* ** pass over the option delimeter */ lpstr++; /* ** Copy option and skip over it. */ lpstr += CopyWord( chOption, lpstr ); /* ** Is this the play option ?? If so, don't bother processing anymore ** options. */ if ( 0 == _tcscmp( chOption, g_szUpdate ) ) { return TRUE; } /* ** Remove leading space */ lpstr += _tcsspn( lpstr, g_szBlank ); } return FALSE; } /*****************************Private*Routine******************************\ * ParseCommandLine * * Here we look to see if we have been asked to play a particular track. * The format of the command line is: * * * CDPlayer command options. * * CDPLAYER {Options} * * Options : -play | {Sub-Options} * Sub-Options : {-track tracklist} | trackname * trackname : filename | drive letter * tracklist : filename+ * * -track A track list if a list of tracks that the user wants * to play. It overides any play list that may already be stored * for the current cd. * * -play Start playing the current play list. If -play is not specified * CD Player seeks to the first track in the play list. * * * On Windows NT the format of [file] is: * d:\track(nn).cda * * where d: is the drive letter of the cdrom that you want to play * and \track(nn) is the track number that you want to play. * * Therefore to play track 8 from a cd-rom drive mapped to e: * * cdplayer /play e:\track08.cda * * On Chicago the file is actually a riff CDDA file which contains * all the info required to locate the disc and track. * * Returns the index of the CD-Rom drive which should be played first. Can return * -1 iff the caller passed FALSE for the fQuiet parameter and the message box was * actually displayed. This should only occur when the user trys to start a new * copy of cdplayer passing it the track.cda file of a disk that is not inserted * in any of the current CD-Drives attached to the machine. * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ int ParseCommandLine( LPTSTR lpstr, int *piTrackToSeekTo, BOOL fQuiet ) { TCHAR chOption[MAX_PATH]; BOOL fTrack = FALSE; int iCdRomIndex = -1; // Assume Failure until proven otherwise int ii; for (ii = 0; ii < g_NumCdDevices; ii++) { g_Devices[ii]->fKilledPlayList = FALSE; } /* ** Start by converting everything to upper case. */ CharUpperBuff( lpstr, _tcslen(lpstr) ); #if DBG #ifdef UNICODE dprintf( "CD Player Command line : %ls", lpstr ); #else dprintf( "CD Player Command line : %s", lpstr ); #endif #endif /* ** The first parameter on the command line is always the ** string used invoke the program. ie cdplayer.exe */ lpstr += _tcsspn( lpstr, g_szBlank ); lpstr += CopyWord( chOption, lpstr ); /* ** Remove leading space */ lpstr += _tcsspn( lpstr, g_szBlank ); /* ** process any command line options */ while ( (*lpstr == g_chOptionHyphen) || (*lpstr == g_chOptionSlash) ) { /* ** pass over the option delimeter */ lpstr++; /* ** Copy option and skip over it. */ lpstr += CopyWord( chOption, lpstr ); /* ** Is this a command line option we understand - ignore ones ** we don't understand. */ if ( 0 == _tcscmp( chOption, g_szTrack ) ) { if ( !fTrack ) { lpstr = ParseTrackList( lpstr, &iCdRomIndex ); fTrack = TRUE; } } else if ( 0 == _tcscmp( chOption, g_szPlay ) ) { g_fPlay = TRUE; } else { #if DBG #ifdef UNICODE dprintf("Ignoring unknown option %ls\n", chOption ); #else dprintf("Ignoring unknown option %s\n", chOption ); #endif #endif } /* ** Remove leading space */ lpstr += _tcsspn( lpstr, g_szBlank ); } /* ** parse remaining command line parameters */ if ( (*lpstr != g_chNULL) && !fTrack) { /* ** Copy track name and skip over it. Sometimes the shell ** gives us quoted strings and sometimes it doesn't. If the ** string is not quoted assume that remainder of the command line ** is the track name. */ if ( (*lpstr == TEXT('\'')) || (*lpstr == TEXT('\"')) ) { lpstr += CopyWord( chOption, lpstr ); } else { _tcscpy(chOption, lpstr); } if ( IsTrackFileNameValid( chOption, &iCdRomIndex, piTrackToSeekTo, FALSE, fQuiet ) ) { ResetPlayList( iCdRomIndex ); } #if DBG #ifdef UNICODE dprintf("Seeking to track %ls\n", chOption ); #else dprintf("Seeking to track %s\n", chOption ); #endif #endif } return iCdRomIndex; } /*****************************Private*Routine******************************\ * ParseTrackList * * Each track is separated by a ' ' character. The track list is terminated * by the NULL, '/' or '-' character. * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ TCHAR * ParseTrackList( TCHAR *szTrackList, int *piCdRomIndex ) { TCHAR chTrack[MAX_PATH]; int iTrackIndex; int iCdRom = -1; // Assume failure, until proven otherwise BOOL fPlayListErased = FALSE; /* ** Remove any stray white space */ szTrackList += _tcsspn( szTrackList, g_szBlank ); /* ** While there are still valid characters to process */ while ( (*szTrackList != g_chNULL) && (*szTrackList != g_chOptionHyphen) && (*szTrackList != g_chOptionSlash) ) { /* ** Copy the track name and skip over it. */ szTrackList += CopyWord( chTrack, szTrackList ); /* ** Now check that we have been given a valid filename */ if ( IsTrackFileNameValid( chTrack, &iCdRom, &iTrackIndex, TRUE, FALSE ) ) { PTRACK_PLAY pt; /* ** If this is the first valid file given nuke the ** existing play lists and prepare for a new list. Note that ** things are complicated by the fact that we could be given ** files from more than one CD-Rom drive. */ if (! g_Devices[iCdRom]->fKilledPlayList) { /* ** Kill the old play and save lists. */ ErasePlayList( iCdRom ); EraseSaveList( iCdRom ); PLAYLIST( iCdRom ) = NULL; SAVELIST( iCdRom ) = NULL; fPlayListErased = TRUE; g_Devices[iCdRom]->fKilledPlayList = TRUE; *piCdRomIndex = iCdRom; } pt = AllocMemory( sizeof(TRACK_PLAY) ); pt->TocIndex = iTrackIndex; pt->min = 0; pt->sec = 0; /* ** Is this the first track on this devices play list ? */ if ( PLAYLIST(iCdRom) == NULL ) { PLAYLIST(iCdRom) = pt; pt->nextplay = pt->prevplay = NULL; } else { /* ** append this track to the end of the current play list */ AppendTrackToPlayList( PLAYLIST(iCdRom), pt ); } } else { /* ** Put up a message box warning the user that the given ** track name is invalid and that we can't play it. */ ; } /* ** Remove any stray white space */ szTrackList += _tcsspn( szTrackList, g_szBlank ); } /* ** If we have erased the play list we have to go off and reset the ** saved play list. */ if ( fPlayListErased ) { SAVELIST( iCdRom ) = CopyPlayList( PLAYLIST(iCdRom) ); } return szTrackList; } /*****************************Private*Routine******************************\ * CopyWord * * Copies one from szSource to szWord - assumes that words are delimited * by ' ' characters. szSource MUST point to the begining of the word. * * Returns length of word copied. * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ int CopyWord( TCHAR *szWord, TCHAR *szSource ) { int n, nReturn; /* ** Copy the track name */ if ( (*szSource == TEXT('\'')) || (*szSource == TEXT('\"')) ) { TCHAR ch = *szSource; /* ** Remember which quote character it was ** According to the DOCS " is invalid in a filename... */ n = 0; /* ** Move over the initial quote, then copy the filename */ while ( *++szSource && *szSource != ch ) { szWord[n++] = *szSource; } nReturn = n + (*szSource == ch ? 2 : 1); } else { n = _tcscspn( szSource, g_szBlank ); _tcsncpy( szWord, szSource, n ); nReturn = n; } szWord[n] = g_chNULL; return nReturn; } /*****************************Private*Routine******************************\ * IsTrackFileNameValid * * This function returns true if the specified filename is a valid CD track. * On NT track filenames must be of the form: * d:\track(n).cda where d: is the CD-Rom device and \track(n).cda * is the index of the track to be played (starting from 1). * * On Chicago the track filename is actually a riff CDDA file which contains * the track info that we require. * * If the filename is valid the function true and sets piCdromIndex and * piTrackIndex to the correct values. * * History: * 29-09-94 - StephenE - Created * \**************************************************************************/ BOOL IsTrackFileNameValid( LPTSTR lpstFileName, int *piCdRomIndex, int *piTrackIndex, BOOL fScanningTracks, BOOL fQuiet ) { #define RIFF_RIFF 0x46464952 #define RIFF_CDDA 0x41444443 RIFFCDA cda; HANDLE hFile; TCHAR chDriveLetter; int i; TCHAR szFileName[MAX_PATH]; TCHAR szPath[MAX_PATH]; SHFILEINFO shInfo; DWORD cbRead; // // If we are not constructing a track play list it is valid to just specify // a drive letter, in which case we select that drive and start playing // at the first track on it. All the tracks are played in sequential // order. // if ( !fScanningTracks) { // // Map the drive letter onto the internal CD-Rom index used by CDPlayer. // chDriveLetter = *lpstFileName; for ( i = 0; i < g_NumCdDevices; i++ ) { if (g_Devices[i]->drive == chDriveLetter) { *piCdRomIndex = i; break; } } // // If we mapped the drive OK check to see if we should play all // the tracks or just the current play list for that drive. If we // didn't map the drive OK assume that its the first part of a // RIFF filename and fall through to the code below that opens the // RIFF file and parses its contents. // if ( i != g_NumCdDevices ) { // // If next character is only a colon ':' then play the // the entire disk starting from the first track. // if ( 0 == _tcscmp(lpstFileName + 1, g_szColon) ) { *piTrackIndex = 0; return TRUE; } // // If the next two characters are colon backslash ":\" then // we seek to the specified drive and play only those tracks that // are in the default playlist for the current disk in that drive. // All we need to do to achive this is return FALSE. // if ( 0 == _tcscmp(lpstFileName + 1, g_szColonBackSlash) ) { return FALSE; } } } // // Otherwise, open the file and read the CDA info. The file name may be a // link to .cda in which case we need to get the shell to resolve the link for // us. We take a copy of the file name because the ResolveLink function // modifies the file name string in place. // _tcscpy(szFileName, lpstFileName); if (0L == SHGetFileInfo( szFileName, 0L, &shInfo, sizeof(shInfo), SHGFI_ATTRIBUTES)) { return FALSE; } if ((shInfo.dwAttributes & SFGAO_LINK) == SFGAO_LINK) { if (!g_fOleInitialized) { g_fOleInitialized = SUCCEEDED(OleInitialize(NULL)); } if (!ResolveLink(szFileName)) { return FALSE; } } // Make sure file exists if (GetFileAttributes (szFileName) == ((DWORD)-1)) { // Get Full path to file if (0 == SearchPath (NULL, szFileName, NULL, MAX_PATH, szPath, NULL)) { return FALSE; } } else { lstrcpy (szPath, szFileName); } // Open file and read in CDA info hFile = CreateFile (szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == hFile) { return FALSE; } ReadFile(hFile, &cda, sizeof(cda), &cbRead, NULL); CloseHandle (hFile); // // Make sure its a RIFF CDDA file // if ( (cda.dwRIFF != RIFF_RIFF) || (cda.dwCDDA != RIFF_CDDA) ) { return FALSE; } // // Make sure that we have this disc loaded. // for ( i = 0; i < g_NumCdDevices; i++ ) { if (g_Devices[i]->CdInfo.Id == cda.DiscID) { *piCdRomIndex = i; break; } } // // If we didn't map the drive OK return FALSE AND set the // returned CD-ROM index to -1 but only if the caller asked us // to complain about an incorrect CD being inserted in the drive. // if ( i == g_NumCdDevices ) { if (!fQuiet) { AskUserToInsertCorrectDisc(cda.DiscID); *piCdRomIndex = -1; } return FALSE; } *piTrackIndex = cda.wTrack - 1; return TRUE; } /*****************************Private*Routine******************************\ * AppendTrackToPlayList * * Appends the TRACK_PLAY record pointed to by pAppend to the end of the * double linked list pointed to by pHead. * * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ void AppendTrackToPlayList( PTRACK_PLAY pHead, PTRACK_PLAY pAppend ) { PTRACK_PLAY pp = pHead; while (pp->nextplay != NULL) { pp = pp->nextplay; } pp->nextplay = pAppend; pAppend->prevplay = pp; pAppend->nextplay = NULL; } /*****************************Private*Routine******************************\ * FindMostSuitableDrive * * Tries to determine the best drive to make the current drive. Returns the * drive. * * We should choose the first disc that is playing if any are playing. * * Else we should choose the first disc with a music disk in it if there * any drives with music discs in them. * * Else we should chose the first drive that is available if any of the * drives are available. * * Else just choose the first (ie. zeroth) drive. * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ int FindMostSuitableDrive( void ) { int iDisc; /* ** Check for a playing drive */ for ( iDisc = 0; iDisc < g_NumCdDevices; iDisc++ ) { if ( g_Devices[iDisc]->State & (CD_PLAYING | CD_PAUSED) ) { return iDisc; } } /* ** Check for a drive with a music disk in it */ for ( iDisc = 0; iDisc < g_NumCdDevices; iDisc++ ) { if ( g_Devices[iDisc]->State & CD_LOADED ) { return iDisc; } } /* ** Check for a drive that is not in use */ for ( iDisc = 0; iDisc < g_NumCdDevices; iDisc++ ) { if ( (g_Devices[iDisc]->State & (CD_BEING_SCANNED | CD_IN_USE)) == 0 ) { return iDisc; } } return 0; } /*****************************Private*Routine******************************\ * AskUserToInsertCorrectDisc * * * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ void AskUserToInsertCorrectDisc( DWORD dwID ) { TCHAR szSection[10]; TCHAR szMsgBoxTitle[32]; TCHAR szDiskTitle[TITLE_LENGTH]; TCHAR szArtistName[ARTIST_LENGTH]; TCHAR szFormat[STR_MAX_STRING_LEN]; TCHAR szText[STR_MAX_STRING_LEN + TITLE_LENGTH]; /* ** Try to find the disk title in the cdplayer disk database. */ wsprintf( szSection, g_szSectionF, dwID ); GetPrivateProfileString( szSection, g_szTitle, g_szNothingThere, szDiskTitle, TITLE_LENGTH, g_IniFileName ); /* ** If the disk title was found in the database display it. */ if (_tcscmp(szDiskTitle, g_szNothingThere) != 0) { TCHAR szUnknown[64]; _tcscpy( szUnknown, IdStr(STR_UNKNOWN_ARTIST) ); GetPrivateProfileString( szSection, g_szArtist, szUnknown, szArtistName, ARTIST_LENGTH, g_IniFileName ); _tcscpy( szFormat, IdStr(STR_DISK_NOT_THERE_K) ); wsprintf(szText, szFormat, szDiskTitle, szArtistName); } else { _tcscpy( szText, IdStr(STR_DISK_NOT_THERE) ); } // // If CD Player is minimized make sure it is restored // before displaying the MessageBox // if (IsIconic(g_hwndApp)) { WINDOWPLACEMENT wndpl; wndpl.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(g_hwndApp, &wndpl); wndpl.showCmd = SW_RESTORE; SetWindowPlacement(g_hwndApp, &wndpl); } _tcscpy( szMsgBoxTitle, IdStr(STR_CDPLAYER) ); MessageBox( g_hwndApp, szText, szMsgBoxTitle, MB_SETFOREGROUND | MB_ICONINFORMATION | MB_APPLMODAL | MB_OK); } #ifndef USE_IOCTLS BOOL CheckMCICDA (TCHAR chDrive) { DWORD cchLen; DWORD dwResult; DWORD dwErr; CDHANDLE hCD; TCHAR szPath[MAX_PATH]; TCHAR szText[512]; TCHAR szTitle[MAX_PATH]; // Make sure the mcicda.dll exists cchLen = NUMELEMS(szPath); dwResult = SearchPath (NULL, TEXT ("mcicda.dll"), NULL, cchLen, szPath, NULL); if ((! dwResult) || (0xFFFFFFFF == GetFileAttributes (szPath))) { // Give Missing MCICDA.DLL error message GetSystemDirectory (szPath, cchLen); _tcscpy( szTitle, IdStr( STR_MCICDA_MISSING ) ); wsprintf (szText, szTitle, szPath); _tcscpy( szTitle, IdStr( STR_CDPLAYER ) ); MessageBox( NULL, szText, szTitle, MB_APPLMODAL | MB_ICONINFORMATION | MB_OK | MB_SETFOREGROUND ); return FALSE; } // Make sure mcicda.dll service is up and running hCD = OpenCdRom (chDrive, &dwErr); if (! hCD) { // Error loading media device driver. _tcscpy( szText, IdStr( STR_MCICDA_NOT_WORKING ) ); _tcscpy( szTitle, IdStr( STR_CDPLAYER ) ); MessageBox( NULL, szText, szTitle, MB_APPLMODAL | MB_ICONINFORMATION | MB_OK | MB_SETFOREGROUND ); return FALSE; } // Close Device CloseCdRom (hCD); return TRUE; } #endif // ! USE_IOCTLS #if DBG /******************************Public*Routine******************************\ * CDAssert * * * History: * 18-11-93 - StephenE - Created * \**************************************************************************/ void CDAssert( LPSTR x, LPSTR file, int line ) { char buff[128]; wsprintfA( buff, "%s \nat line %d of %s", x, line, file ); MessageBoxA( NULL, buff, "Assertion Failure:", MB_APPLMODAL | MB_OK ); } /******************************Public*Routine******************************\ * dprintf * * * * History: * dd-mm-94 - StephenE - Created * \**************************************************************************/ void dprintf( char *lpszFormat, ... ) { char buf[512]; UINT n; va_list va; static int iPrintOutput = -1; if (iPrintOutput == -1) { iPrintOutput = GetProfileInt( TEXT("MMDEBUG"), TEXT("CdPlayer"), 0); } if (iPrintOutput) { n = wsprintfA(buf, "CdPlayer: <%d>", GetCurrentThreadId() ); va_start(va, lpszFormat); n += wvsprintfA(buf+n, lpszFormat, va); va_end(va); buf[n++] = '\n'; buf[n] = 0; OutputDebugStringA(buf); } } #endif // End #ifdef DBG