// speakn.cpp : Defines the class behaviors for the SpeakN application. // // This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992-1998 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #include "speakn.h" #include ///////////////////////////////////////////////////////////////////////////// // Sound helpers static void PlaySound(LPCTSTR lpszSound) { HRSRC hRes; // resource handle to wave file HGLOBAL hData; BOOL bOk = FALSE; if ((hRes = ::FindResource(AfxGetResourceHandle(), lpszSound, _T("sound"))) != NULL && (hData = ::LoadResource(AfxGetResourceHandle(), hRes)) != NULL) { // found the resource, play it bOk = sndPlaySound((LPCTSTR)::LockResource(hData), SND_MEMORY|SND_SYNC|SND_NODEFAULT); FreeResource(hData); } if (!bOk) { static BOOL bReported = FALSE; if (!bReported) { AfxMessageBox(IDS_CANNOT_PLAY_SOUND); bReported = TRUE; // once please } } } inline static void PlaySound(UINT nIDS) { PlaySound(MAKEINTRESOURCE(nIDS)); } ///////////////////////////////////////////////////////////////////////////// // CSpeakNDlg CSpeakNDlg::CSpeakNDlg(BOOL bNoPen) : CDialog(bNoPen ? IDD_NOPENDIALOG : CSpeakNDlg::IDD) { //{{AFX_DATA_INIT(CSpeakNDlg) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT m_bNoPen = bNoPen; m_lpszNextQuestion = NULL; m_bNoAnswerCheck = FALSE; } BEGIN_MESSAGE_MAP(CSpeakNDlg, CDialog) //{{AFX_MSG_MAP(CSpeakNDlg) ON_COMMAND(IDC_REPLAY_SOUND, OnReplaySound) ON_COMMAND(IDC_GIVE_UP, OnGiveUp) ON_COMMAND(IDC_PICTURE, OnReplaySound) ON_EN_CHANGE(IDC_INPUT_EDIT, OnUpdateStatus) //}}AFX_MSG_MAP END_MESSAGE_MAP() BOOL CSpeakNDlg::LoadLesson(LPCTSTR lpLessonName) { // load lesson from resource HRSRC hRes; // resource handle to lesson data HGLOBAL hData; if ((hRes = ::FindResource(AfxGetResourceHandle(), lpLessonName, _T("lesson"))) == NULL || (hData = ::LoadResource(AfxGetResourceHandle(), hRes)) == NULL) return FALSE; m_lpszNextQuestion = (LPCSTR)::LockResource(hData); return TRUE; } void CSpeakNDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CSpeakNDlg) DDX_Control(pDX, IDOK, m_buttonNext); //}}AFX_DATA_MAP } BOOL CSpeakNDlg::OnInitDialog() { UpdateData(FALSE); // call DoDataExchange to initialize m_buttonNext m_buttonNext.EnableWindow(FALSE); ASSERT(m_targetWord.IsEmpty()); // not started yet // set the font of the prompt text to something bigger LOGFONT logfont; memset(&logfont, 0, sizeof(logfont)); logfont.lfHeight = 40; logfont.lfWeight = FW_BOLD; CString szFont; szFont.LoadString(IDS_FONT_NAME); // TrueType font lstrcpy(logfont.lfFaceName, szFont); VERIFY(m_biggerFont.CreateFontIndirect(&logfont)); PromptText().SetFont(&m_biggerFont); InputEdit().SetFont(&m_biggerFont); // load the bitmaps for bitmap buttons VERIFY(m_replayButton.AutoLoad(IDC_REPLAY_SOUND, this)); InputEdit().ShowWindow(FALSE); // start with input disabled // load initial picture VERIFY(m_pictureButton.SubclassDlgItem(IDC_PICTURE, this)); VERIFY(m_pictureButton.LoadBitmaps(_T("intro"), NULL, NULL)); // Make the dialog visible, and update ShowWindow(TRUE); // SHOW_OPENWINDOW UpdateWindow(); PlaySound(IDSOUND_WELCOME); AdvanceLesson(); return FALSE; // focus already set } void CSpeakNDlg::OnReplaySound() { InputEdit().SetFocus(); PlaySound(m_targetRes); } void CSpeakNDlg::OnOK() { // check results CString result; InputEdit().GetWindowText(result); if (result != m_targetWord) { PlaySound(IDSOUND_INCORRECT); AfxMessageBox(IDS_TRY_AGAIN); return; } PlaySound(IDSOUND_CORRECT); AdvanceLesson(); } void CSpeakNDlg::OnGiveUp() { PlaySound(IDSOUND_GIVEUP); SetAnswerText(m_targetWord); // show answer OnReplaySound(); AdvanceLesson(); } void CSpeakNDlg::SetAnswerText(LPCTSTR lpsz) { // setting the window text for an edit control will cause EN_CHANGE // control notifications, so we lock them out while setting the // text programmatically ASSERT(!m_bNoAnswerCheck); m_bNoAnswerCheck = TRUE; InputEdit().SetWindowText(lpsz); m_bNoAnswerCheck = FALSE; } ///////////////////////////////////////////////////////////////////////////// // Advancing to the next lesson void CSpeakNDlg::AdvanceLesson() { if (*m_lpszNextQuestion == '\0') { // out of questions PlaySound(IDSOUND_GOODBYE); EndDialog(IDOK); return; } // The lesson resource consists of several pairs of words (that the user guesses) // and bitmap resource names. The words and resource names are stored in the // resource as ANSI text. This text must be converted to UNICODE. #ifdef _UNICODE TCHAR szT[20]; int nLen; nLen = strlen(m_lpszNextQuestion); mbstowcs(szT, m_lpszNextQuestion, nLen); szT[nLen] = 0; m_targetWord = szT; m_lpszNextQuestion += nLen + 1; nLen = strlen(m_lpszNextQuestion); mbstowcs(szT, m_lpszNextQuestion, nLen); szT[nLen] = 0; m_targetRes = szT; m_lpszNextQuestion += nLen + 1; #else m_targetWord = m_lpszNextQuestion; m_lpszNextQuestion += m_targetWord.GetLength() + 1; m_targetRes = m_lpszNextQuestion; m_lpszNextQuestion += m_targetRes.GetLength() + 1; #endif m_targetWord.MakeUpper(); m_targetRes.MakeUpper(); PlaySound(IDSOUND_QUESTION); // draw the picture (bitmap with the same name as the target) if (!m_pictureButton.LoadBitmaps(m_targetRes)) { AfxMessageBox(IDS_PICTURE_UNAVAILABLE); VERIFY(m_pictureButton.LoadBitmaps(_T("intro"), NULL, NULL)); // go back to the initial bitmap } m_pictureButton.Invalidate(TRUE); SetAnswerText(_T("")); InputEdit().ShowWindow(TRUE); OnUpdateStatus(); // set appropriate face OnReplaySound(); // ask question if (*m_lpszNextQuestion == '\0') { CString strDone; strDone.LoadString(IDS_DONE); m_buttonNext.SetWindowText(strDone); } } ///////////////////////////////////////////////////////////////////////////// // Happy face status indicator void CSpeakNDlg::OnUpdateStatus() { CString result; InputEdit().GetWindowText(result); UINT nIDI = IDI_FACE_NEUTRAL; // default if (result == m_targetWord) { m_buttonNext.EnableWindow(); nIDI = IDI_FACE_HAPPIER; // exact match } else { m_buttonNext.EnableWindow(FALSE); if (result.IsEmpty()) nIDI = IDI_FACE_NEUTRAL; // not started yet else { nIDI = (_tcsncmp(m_targetWord, result, _tcslen(result)) == 0? IDI_FACE_HAPPY : IDI_FACE_SAD); } } HICON hNew = ::LoadIcon(AfxGetResourceHandle(), MAKEINTRESOURCE(nIDI)); ASSERT(hNew != NULL); ::DestroyIcon(StatusFace().SetIcon(hNew)); UpdateWindow(); // draw everything now if (m_bNoAnswerCheck) return; // don't update if (nIDI == IDI_FACE_HAPPIER) { // exact match - automatic advance OnReplaySound(); PlaySound(IDSOUND_CORRECT); } } ///////////////////////////////////////////////////////////////////////////// // CSpeakNApp BOOL CSpeakNApp::InitInstance() { Enable3dControls(); BOOL bNoPen = TRUE; // no pen-aware controls // Creates a simple dialog and do it CSpeakNDlg mainDlg(bNoPen); if (!mainDlg.LoadLesson(_T("SAMPLE1"))) return FALSE; m_pMainWnd = &mainDlg; mainDlg.DoModal(); // that's all, quit app ::PostQuitMessage(0); return TRUE; } CSpeakNApp NEAR theApp; /////////////////////////////////////////////////////////////////////////////