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

2431 lines
80 KiB

  1. #include "stdafx.h"
  2. #include "resource.h"
  3. #include <sapi.h>
  4. #include <string.h>
  5. #include "SRDlg.h"
  6. #include <spddkhlp.h>
  7. #include <initguid.h>
  8. #include "helpresource.h"
  9. #include "richedit.h"
  10. #include "mlang.h"
  11. #include "Lmcons.h"
  12. static DWORD aKeywordIds[] = {
  13. // Control ID // Help Context ID
  14. IDC_ADD, IDH_ADD,
  15. IDC_MODIFY, IDH_SETTINGS,
  16. IDC_DELETE, IDH_DELETE,
  17. IDC_TRN_ADVICE, IDH_NOHELP,
  18. IDC_USER, IDH_USER,
  19. IDC_MIC_ICON, IDH_NOHELP,
  20. IDC_COMBO_RECOGNIZERS, IDH_ENGINES,
  21. IDC_SR_ADV, IDH_SR_ADV,
  22. IDC_USERTRAINING, IDH_USERTRAINING,
  23. IDC_PROGRESS1, IDH_PROGRESS1,
  24. IDC_AUD_IN, IDH_AUD_IN,
  25. IDC_MICWIZ, IDH_MICWIZ,
  26. IDC_SR_ICON, IDH_NOHELP,
  27. IDC_SR_CAPTION, IDH_NOHELP,
  28. IDC_SR_LIST_CAP, IDH_NOHELP,
  29. IDC_TRAIN_GROUP, IDH_NOHELP,
  30. IDC_ADVICE, IDH_NOHELP,
  31. IDC_IN_GROUP, IDH_NOHELP,
  32. IDC_MIC_CAP, IDH_NOHELP,
  33. IDC_MIC_INST, IDH_NOHELP,
  34. 0, 0
  35. };
  36. /*****************************************************************************
  37. * CSRDlg::CreateRecoContext *
  38. *---------------------------*
  39. * Description:
  40. * This creates a new instance of the recognizer with whatever is the
  41. * current defaults for the recognizer.
  42. * The "fInitialize" argument is FALSE by default. If set, it does
  43. * NOT attempt to set the m_pCurUserToken reco profile and instead
  44. * just picks up whatever CoCreateInstance() on the shared recognizer
  45. * gave it.
  46. * NOTE: The caller is responsible for displaying error messages to
  47. * the user when this fails.
  48. * Return:
  49. * S_OK
  50. * Failed HRESULT from recognizer/recocontext initialization functions
  51. ****************************************************************** BECKYW ***/
  52. HRESULT CSRDlg::CreateRecoContext(BOOL *pfContextInitialized, BOOL fInitialize, ULONG ulFlags)
  53. {
  54. // Kill the reco context and notify sink first, if we have one
  55. if ( m_cpRecoCtxt )
  56. {
  57. m_cpRecoCtxt->SetNotifySink( NULL );
  58. }
  59. m_cpRecoCtxt.Release();
  60. HRESULT hr;
  61. // SOFTWARE ENGINEERING OPPORTUNITY (beckyw 8/24): This is a workaround for a
  62. // bug that appears to repro only on my dev machine, in which the recostate
  63. // needs to be inactive for this whole thing.
  64. if ( m_cpRecoEngine )
  65. {
  66. m_cpRecoEngine->SetRecoState( SPRST_INACTIVE );
  67. }
  68. if ( m_cpRecoEngine )
  69. {
  70. SPRECOSTATE recostate;
  71. hr = m_cpRecoEngine->GetRecoState( &recostate );
  72. // This is due to a SOFTWARE ENGINEERING OPPORTUNITY in which SetRecognizer( NULL )
  73. // doesn't work if the recostate is SPRST_ACTIVE_ALWAYS.
  74. // In this case, we temporarily switch the recostate
  75. if ( SUCCEEDED( hr ) && (SPRST_ACTIVE_ALWAYS == recostate) )
  76. {
  77. hr = m_cpRecoEngine->SetRecoState( SPRST_INACTIVE );
  78. }
  79. // Kick the recognizer
  80. if ( SUCCEEDED( hr ) && (ulFlags & SRDLGF_RECOGNIZER) )
  81. {
  82. hr = m_cpRecoEngine->SetRecognizer( NULL );
  83. }
  84. // Kick the audio input
  85. if ( SUCCEEDED( hr ) && (ulFlags & SRDLGF_AUDIOINPUT))
  86. {
  87. hr = m_cpRecoEngine->SetInput( NULL, TRUE );
  88. }
  89. // Set the recostate back if we changed it.
  90. if ( (SPRST_ACTIVE_ALWAYS == recostate) )
  91. {
  92. HRESULT hrRecoState = m_cpRecoEngine->SetRecoState( recostate );
  93. if ( FAILED( hrRecoState ) )
  94. {
  95. hr = hrRecoState;
  96. }
  97. }
  98. }
  99. else
  100. {
  101. hr = m_cpRecoEngine.CoCreateInstance( CLSID_SpSharedRecognizer );
  102. }
  103. if(!fInitialize && SUCCEEDED( hr ))
  104. {
  105. // Normally set to m_pCurUserToken
  106. // When initializing this is not created yet so just set to default
  107. hr = m_cpRecoEngine->SetRecoProfile(m_pCurUserToken);
  108. }
  109. if ( SUCCEEDED( hr ) )
  110. {
  111. hr = m_cpRecoEngine->CreateRecoContext(&m_cpRecoCtxt);
  112. }
  113. if ( SUCCEEDED( hr ) )
  114. {
  115. hr = m_cpRecoCtxt->SetNotifyWindowMessage(m_hDlg, WM_RECOEVENT, 0, 0);
  116. }
  117. if ( SUCCEEDED( hr ) )
  118. {
  119. const ULONGLONG ullInterest = SPFEI(SPEI_SR_AUDIO_LEVEL);
  120. hr = m_cpRecoCtxt->SetInterest(ullInterest, ullInterest);
  121. }
  122. // Set the pfContextInitialized flag if everything has gone OK;
  123. // if something has not gone OK, clean up
  124. if ( pfContextInitialized )
  125. {
  126. // If we got here, the reco context has been initialized
  127. *pfContextInitialized = SUCCEEDED( hr );
  128. }
  129. if ( FAILED( hr ))
  130. {
  131. m_cpRecoCtxt.Release();
  132. m_cpRecoEngine.Release();
  133. return hr;
  134. }
  135. #ifdef _DEBUG
  136. // Let's make sure we actually have the right recognizer now
  137. CComPtr<ISpObjectToken> cpCurDefaultToken; // What it should be
  138. SpGetDefaultTokenFromCategoryId(SPCAT_RECOGNIZERS, &cpCurDefaultToken);
  139. CComPtr<ISpObjectToken> cpRecognizerToken;
  140. m_cpRecoEngine->GetRecognizer( &cpRecognizerToken );
  141. if ( cpRecognizerToken )
  142. {
  143. CSpDynamicString dstrCurDefaultToken;
  144. cpCurDefaultToken->GetId( &dstrCurDefaultToken );
  145. CSpDynamicString dstrRecognizerToken;
  146. cpRecognizerToken->GetId( &dstrRecognizerToken );
  147. if ( 0 != wcsicmp( dstrCurDefaultToken, dstrRecognizerToken ) )
  148. {
  149. OutputDebugString( L"Warning: We just created a recognizer that isn't the default!\n" );
  150. }
  151. }
  152. #endif
  153. // Now turn on the reco state for the volume meter
  154. hr = m_cpRecoEngine->SetRecoState( SPRST_ACTIVE_ALWAYS );
  155. return(hr);
  156. }
  157. /*****************************************************************************
  158. * SortCols *
  159. *-----------*
  160. * Description:
  161. * Comparison function for subitems in the reco list
  162. ****************************************************************** BRENTMID ***/
  163. int CALLBACK SortCols( LPARAM pToken1, LPARAM pToken2, LPARAM pDefToken )
  164. {
  165. USES_CONVERSION;
  166. // Get the names
  167. CSpDynamicString dstrDesc1;
  168. CSpDynamicString dstrDesc2;
  169. SpGetDescription( (ISpObjectToken *) pToken1, &dstrDesc1 );
  170. SpGetDescription( (ISpObjectToken *) pToken2, &dstrDesc2 );
  171. // First check if there is no description for either one.
  172. // If there is no description, set it to "<no name>"
  173. if ( !dstrDesc1.m_psz || !dstrDesc2.m_psz )
  174. {
  175. WCHAR szNoName[ MAX_LOADSTRING ];
  176. szNoName[0] = 0;
  177. ::LoadString( _Module.GetResourceInstance(), IDS_UNNAMED_RECOPROFILE, szNoName, sp_countof( szNoName ) );
  178. USES_CONVERSION;
  179. if ( !dstrDesc1 )
  180. {
  181. dstrDesc1 = szNoName;
  182. SpSetDescription( (ISpObjectToken *) pToken1, dstrDesc1 );
  183. }
  184. if ( !dstrDesc2 )
  185. {
  186. dstrDesc2 = szNoName;
  187. SpSetDescription( (ISpObjectToken *) pToken2, dstrDesc2 );
  188. }
  189. }
  190. if (pDefToken == pToken1) {
  191. return -1; // make sure pToken1 goes to top of list
  192. }
  193. else if (pDefToken == pToken2) {
  194. return 1; // make sure pToken2 goes to top of list
  195. }
  196. return wcscmp(_wcslwr(dstrDesc1.m_psz), _wcslwr(dstrDesc2.m_psz));
  197. }
  198. /*****************************************************************************
  199. * CSRDlg::RecoEvent *
  200. *-------------------*
  201. * Description:
  202. * Handles the SR events for the volume meter
  203. ****************************************************************** BRENTMID ***/
  204. void CSRDlg::RecoEvent()
  205. {
  206. CSpEvent event;
  207. if (m_cpRecoCtxt)
  208. {
  209. while (event.GetFrom(m_cpRecoCtxt) == S_OK)
  210. {
  211. if (event.eEventId == SPEI_SR_AUDIO_LEVEL)
  212. {
  213. ULONG l = (ULONG)event.wParam;
  214. SendMessage( GetDlgItem ( m_hDlg, IDC_PROGRESS1 ), PBM_SETPOS, l, 0);
  215. }
  216. }
  217. }
  218. }
  219. /*****************************************************************************
  220. * TrySwitchDefaultEngine *
  221. *------------------------*
  222. * Description:
  223. * This function is called when we want to run some UI for the engine
  224. * the user has selected, but because we don't know which shared engine
  225. * is running and whether another app is using it we can't directly
  226. * create the UI. So this method temporarily switches the default recognizer,
  227. * and recreates the engine, and then checks its token. If another app
  228. * was using the engine we wouldn't be able to switch and we return S_FALSE.
  229. * A side effect of this method is that for the duration of the UI, the
  230. * default will be changed, even though the user hasn't yet pressed apply,
  231. * but there seems no good way round this.
  232. *
  233. * In the case that m_pCurRecoToken is actually the same token as the
  234. * one the currently-active recognizer uses, we don't need to create
  235. * a new recognizer and recocontext; instead we just return successfully.
  236. * Return:
  237. * S_OK
  238. * FAILED HRESULT of various functions
  239. * In particular, SPERR_ENGINE_BUSY means that someone else is
  240. * running the engine, so this couldn't be done.
  241. ****************************************************************** DAVEWOOD ***/
  242. HRESULT CSRDlg::TrySwitchDefaultEngine( bool fShowErrorMessages)
  243. {
  244. HRESULT hr = S_OK;
  245. bool fMatch = false;
  246. // Set the new temporary default
  247. if(SUCCEEDED(hr))
  248. {
  249. hr = SpSetDefaultTokenForCategoryId(SPCAT_RECOGNIZERS, m_pCurRecoToken);
  250. }
  251. if ( SUCCEEDED( hr ) && IsRecoTokenCurrentlyBeingUsed( m_pCurRecoToken ) )
  252. {
  253. // No need to switch engine all over again: just keep the one in use
  254. return S_OK;
  255. }
  256. // Try to create the engine & context with the default
  257. // then see if this was actually the engine we expected
  258. if(SUCCEEDED(hr))
  259. {
  260. hr = CreateRecoContext( );
  261. }
  262. if ( FAILED( hr ) && fShowErrorMessages )
  263. {
  264. WCHAR szError[256];
  265. szError[0] = '\0';
  266. // What to complain about...
  267. UINT uiErrorID = HRESULTToErrorID( hr );
  268. if ( uiErrorID )
  269. {
  270. LoadString(_Module.GetResourceInstance(),
  271. uiErrorID,
  272. szError, sizeof(szError));
  273. MessageBox(g_pSRDlg->m_hDlg, szError, m_szCaption, MB_ICONWARNING|g_dwIsRTLLayout);
  274. }
  275. }
  276. return hr;
  277. }
  278. /*****************************************************************************
  279. * CSRDlg::ResetDefaultEngine *
  280. *----------------------------*
  281. * Description:
  282. * This function resets the engine default back to its original value.
  283. * If the engine already has the right token, it doesn't bother trying
  284. * to create the engine again and returns S_OK
  285. * Return:
  286. * S_OK
  287. * S_FALSE if the default was set back but no engine was created
  288. * FAILED HRESULT of SpSetDefaultTokenForCategoryId()
  289. ****************************************************************** DAVEWOOD ***/
  290. HRESULT CSRDlg::ResetDefaultEngine( bool fShowErrorMessages )
  291. {
  292. HRESULT hr = S_OK;
  293. // Reset the old default
  294. if(m_pDefaultRecToken)
  295. {
  296. hr = SpSetDefaultTokenForCategoryId(SPCAT_RECOGNIZERS, m_pDefaultRecToken);
  297. }
  298. HRESULT hrRet = hr;
  299. BOOL fContextInitialized = FALSE;
  300. if ( SUCCEEDED( hr ) )
  301. {
  302. if ( IsRecoTokenCurrentlyBeingUsed( m_pDefaultRecToken ) )
  303. {
  304. // No need to switch engine all over again: just keep the one in use
  305. if ( m_cpRecoCtxt )
  306. {
  307. fContextInitialized = TRUE;
  308. }
  309. else
  310. {
  311. hr = SPERR_UNINITIALIZED;
  312. }
  313. // The UI might have monkeyed with the recostate.
  314. // Just in case, let's set it back to ACTIVE_ALWAYS
  315. if ( SUCCEEDED( hr ) )
  316. {
  317. hr = m_cpRecoEngine->SetRecoState( SPRST_ACTIVE_ALWAYS );
  318. }
  319. }
  320. else
  321. {
  322. // Create the engine & context using the old default
  323. hr = g_pSRDlg->CreateRecoContext( &fContextInitialized );
  324. }
  325. }
  326. if ( FAILED( hr ) )
  327. {
  328. BOOL fContextInitialized = FALSE;
  329. hr = g_pSRDlg->CreateRecoContext( &fContextInitialized );
  330. // Let's not complain about unsupported languages twice as this may be confusing
  331. // to the user.
  332. if ( FAILED( hr ) && ( SPERR_UNSUPPORTED_LANG != hr ) )
  333. {
  334. RecoContextError( fContextInitialized, fShowErrorMessages, hr );
  335. // The default was set back but no engine was successfully set up.
  336. // A FAILED hresult is not necessary here since the user
  337. // has been notified of the error
  338. hrRet = S_FALSE;
  339. }
  340. // Gray out all the buttons
  341. ::EnableWindow(::GetDlgItem(m_hDlg, IDC_USERTRAINING), FALSE);
  342. ::EnableWindow(::GetDlgItem(m_hDlg, IDC_MICWIZ), FALSE);
  343. ::EnableWindow(::GetDlgItem(m_hDlg, IDC_SR_ADV), FALSE);
  344. ::EnableWindow(::GetDlgItem(m_hDlg, IDC_MODIFY), FALSE);
  345. }
  346. return hrRet;
  347. } /* CSRDlg::ResetDefaultEngine */
  348. /*****************************************************************************
  349. * CSRDlg::IsRecoTokenCurrentlyBeingUsed *
  350. *---------------------------------------*
  351. * Description:
  352. * Call GetRecognizer() on the recognizer currently in use, and
  353. * compare IDs
  354. ****************************************************************** BECKYW ****/
  355. bool CSRDlg::IsRecoTokenCurrentlyBeingUsed( ISpObjectToken *pRecoToken )
  356. {
  357. if ( !pRecoToken || !m_cpRecoEngine )
  358. {
  359. return false;
  360. }
  361. CComPtr<ISpObjectToken> cpRecoTokenInUse;
  362. HRESULT hr = m_cpRecoEngine->GetRecognizer( &cpRecoTokenInUse );
  363. CSpDynamicString dstrTokenID;
  364. CSpDynamicString dstrTokenInUseID;
  365. if ( SUCCEEDED( hr ) )
  366. {
  367. hr = pRecoToken->GetId( &dstrTokenID );
  368. }
  369. if ( SUCCEEDED( hr ) )
  370. {
  371. hr = cpRecoTokenInUse->GetId( &dstrTokenInUseID );
  372. }
  373. return ( SUCCEEDED( hr ) && (0 == wcscmp(dstrTokenID, dstrTokenInUseID)) );
  374. } /* CSRDlg::IsRecoTokenCurrentlyBeingUsed */
  375. /*****************************************************************************
  376. * CSRDlg::HasRecognizerChanged *
  377. *------------------------------*
  378. * Description:
  379. * Look at the currently-requested default recognizer, compare against the
  380. * original default recognizer, and return true iff it is
  381. * different
  382. ****************************************************************** BECKYW ****/
  383. bool CSRDlg::HasRecognizerChanged()
  384. {
  385. bool fChanged = false;
  386. // Check the recognizer token
  387. CSpDynamicString dstrCurDefaultRecognizerID;
  388. CSpDynamicString dstrCurSelectedRecognizerID;
  389. HRESULT hr = E_FAIL;
  390. if ( m_pDefaultRecToken )
  391. {
  392. hr = m_pDefaultRecToken->GetId( &dstrCurDefaultRecognizerID );
  393. }
  394. if ( SUCCEEDED( hr ) && m_pCurRecoToken )
  395. {
  396. hr = m_pCurRecoToken->GetId( &dstrCurSelectedRecognizerID );
  397. }
  398. if (SUCCEEDED( hr ) && ( 0 != wcsicmp( dstrCurDefaultRecognizerID, dstrCurSelectedRecognizerID ) ))
  399. {
  400. fChanged = true;
  401. }
  402. return fChanged;
  403. } /* CSRDlg::HasRecognizerChanged */
  404. /*****************************************************************************
  405. * CSRDlg::KickCPLUI *
  406. *-------------------*
  407. * Description:
  408. * Look at the currently-requested defaults, compare against the
  409. * original defaults, and enable the Apply button iff anything is
  410. * different
  411. ****************************************************************** BECKYW ****/
  412. void CSRDlg::KickCPLUI()
  413. {
  414. // Check the default recognizer token
  415. bool fChanged = HasRecognizerChanged();
  416. // Check the default user token
  417. CSpDynamicString dstrCurSelectedProfileID;
  418. HRESULT hr = E_FAIL;
  419. if ( m_pCurUserToken )
  420. {
  421. hr = m_pCurUserToken->GetId( &dstrCurSelectedProfileID );
  422. }
  423. if (SUCCEEDED( hr ) && m_dstrOldUserTokenId
  424. && ( 0 != wcsicmp( dstrCurSelectedProfileID, m_dstrOldUserTokenId ) ))
  425. {
  426. fChanged = true;
  427. }
  428. // Check the audio input device
  429. if ( m_pAudioDlg && m_pAudioDlg->IsAudioDeviceChanged() )
  430. {
  431. fChanged = true;
  432. }
  433. // If any tokens have been deleted, there has been a change
  434. if ( m_iDeletedTokens > 0 )
  435. {
  436. fChanged = true;
  437. }
  438. // If any tokens have been added, there has been a change
  439. if ( m_iAddedTokens > 0 )
  440. {
  441. fChanged = true;
  442. }
  443. // Tell the main propsheet
  444. HWND hwndParent = ::GetParent( m_hDlg );
  445. ::SendMessage( hwndParent,
  446. fChanged ? PSM_CHANGED : PSM_UNCHANGED, (WPARAM)(m_hDlg), 0 );
  447. } /* CSRDlg::KickCPLUI */
  448. /*****************************************************************************
  449. * CSRDlg::RecoContextError *
  450. *--------------------------*
  451. * Description:
  452. * Reacts to an error generated by trying to create and set up the
  453. * recognition context within the CPL by displaying an error message
  454. * and graying out the UI.
  455. ****************************************************************** BECKYW ****/
  456. void CSRDlg::RecoContextError( BOOL fRecoContextExists, BOOL fGiveErrorMessage,
  457. HRESULT hrRelevantError )
  458. {
  459. // Complain about the appropriate problem, if needed
  460. if ( fGiveErrorMessage )
  461. {
  462. WCHAR szError[256];
  463. szError[0] = '\0';
  464. // Figure out what error to talk about
  465. UINT uiErrorID = 0;
  466. if ( fRecoContextExists )
  467. {
  468. // There is a reco context but it couldn't be turned on
  469. uiErrorID = IDS_METER_WARNING;
  470. }
  471. else
  472. {
  473. uiErrorID = HRESULTToErrorID( hrRelevantError );
  474. }
  475. if ( uiErrorID )
  476. {
  477. LoadString(_Module.GetResourceInstance(), uiErrorID,
  478. szError, sizeof(szError));
  479. MessageBox(m_hDlg, szError, m_szCaption, MB_ICONWARNING|g_dwIsRTLLayout);
  480. }
  481. }
  482. // Gray out all the buttons
  483. if ( !fRecoContextExists )
  484. {
  485. ::EnableWindow(::GetDlgItem(m_hDlg, IDC_USERTRAINING), FALSE);
  486. ::EnableWindow(::GetDlgItem(m_hDlg, IDC_MICWIZ), FALSE);
  487. ::EnableWindow(::GetDlgItem(m_hDlg, IDC_SR_ADV), FALSE);
  488. ::EnableWindow(::GetDlgItem(m_hDlg, IDC_MODIFY), FALSE);
  489. }
  490. } /* CSRDlg::RecoContextError */
  491. /*****************************************************************************
  492. * CSRDlg::HRESULTToErrorID *
  493. *--------------------------*
  494. * Description:
  495. * Translates a failed HRESULT from a recognizer/recocontext
  496. * initializion into a resource string ID
  497. ****************************************************************** BECKYW ****/
  498. UINT CSRDlg::HRESULTToErrorID( HRESULT hr )
  499. {
  500. if ( SUCCEEDED( hr ) )
  501. {
  502. return 0;
  503. }
  504. // What to complain about...
  505. UINT uiErrorID;
  506. switch( hr )
  507. {
  508. case SPERR_ENGINE_BUSY:
  509. uiErrorID = IDS_ENGINE_IN_USE_WARNING;
  510. break;
  511. case SPERR_UNSUPPORTED_LANG:
  512. uiErrorID = IDS_UNSUPPORTED_LANG;
  513. break;
  514. default:
  515. // Generic error
  516. uiErrorID = IDS_ENGINE_SWITCH_ERROR;
  517. break;
  518. }
  519. return uiErrorID;
  520. } /* CSRDlg::HRESULTToErrorID */
  521. /*****************************************************************************
  522. * CSRDlg::IsProfileNameInvisible *
  523. *--------------------------------*
  524. * Description:
  525. * A profile name is "invisible" iff it is the name of an existing
  526. * profile AND it is on the pending deletes list AND it is does not
  527. * exist for any tokens off the pending deletes list
  528. ****************************************************************** BECKYW ****/
  529. bool CSRDlg::IsProfileNameInvisible( WCHAR *pwszProfile )
  530. {
  531. if ( !pwszProfile )
  532. {
  533. return false;
  534. }
  535. bool fIsInvisible = false;
  536. for ( int i=0; !fIsInvisible && (i < m_iDeletedTokens); i++ )
  537. {
  538. ISpObjectToken *pDeletedToken = m_aDeletedTokens[i];
  539. if ( !pDeletedToken )
  540. {
  541. continue;
  542. }
  543. CSpDynamicString dstrDeletedDesc;
  544. HRESULT hr = SpGetDescription( pDeletedToken, &dstrDeletedDesc );
  545. if ( FAILED( hr ) )
  546. {
  547. continue;
  548. }
  549. if ( 0 == wcscmp( dstrDeletedDesc, pwszProfile ) )
  550. {
  551. bool fOnList = false;
  552. // Now go through everything on the recoprofile list
  553. // that is visible to the user
  554. int cItems = ListView_GetItemCount( m_hUserList );
  555. for ( int j=0; !fOnList && (j < cItems); j++ )
  556. {
  557. LVITEM lvitem;
  558. ::memset( &lvitem, 0, sizeof( lvitem ) );
  559. lvitem.iItem = j;
  560. lvitem.mask = LVIF_PARAM;
  561. BOOL fSuccess = ListView_GetItem( m_hUserList, &lvitem );
  562. ISpObjectToken *pVisibleToken =
  563. fSuccess ? (ISpObjectToken *) lvitem.lParam : NULL;
  564. if ( pVisibleToken )
  565. {
  566. CSpDynamicString dstrVisible;
  567. hr = SpGetDescription( pVisibleToken, &dstrVisible );
  568. if ( SUCCEEDED( hr ) &&
  569. (0 == wcscmp( dstrVisible, pwszProfile )) )
  570. {
  571. fOnList = true;
  572. }
  573. }
  574. }
  575. if ( !fOnList )
  576. {
  577. // The name matches something on the deleted list,
  578. // but it appears nowhere on the list of profiles visible
  579. // to the user.
  580. fIsInvisible = true;
  581. }
  582. }
  583. }
  584. return fIsInvisible;
  585. } /* IsProfileNameInvisible */
  586. /*****************************************************************************
  587. * SRDlgProc *
  588. *-----------*
  589. * Description:
  590. * DLGPROC for managing recognition engines
  591. ****************************************************************** MIKEAR ***/
  592. BOOL CALLBACK SRDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  593. {
  594. SPDBG_FUNC( "SRDlgProc" );
  595. USES_CONVERSION;
  596. switch (uMsg)
  597. {
  598. case WM_RECOEVENT:
  599. {
  600. g_pSRDlg->RecoEvent();
  601. break;
  602. }
  603. case WM_DRAWITEM: // draw the items
  604. {
  605. g_pSRDlg->OnDrawItem( hWnd, ( DRAWITEMSTRUCT * )lParam );
  606. break;
  607. }
  608. case WM_INITDIALOG:
  609. {
  610. g_pSRDlg->OnInitDialog(hWnd);
  611. break;
  612. }
  613. case WM_DESTROY:
  614. {
  615. g_pSRDlg->OnDestroy();
  616. break;
  617. }
  618. // Handle the context sensitive help
  619. case WM_CONTEXTMENU:
  620. {
  621. WinHelp((HWND) wParam, CPL_HELPFILE, HELP_CONTEXTMENU, (DWORD_PTR)(LPWSTR) aKeywordIds);
  622. break;
  623. }
  624. case WM_HELP:
  625. {
  626. WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, CPL_HELPFILE, HELP_WM_HELP,(DWORD_PTR)(LPWSTR) aKeywordIds);
  627. break;
  628. }
  629. case WM_NOTIFY:
  630. switch (((NMHDR*)lParam)->code)
  631. {
  632. case LVN_DELETEITEM:
  633. {
  634. if (wParam != IDC_USER)
  635. {
  636. break;
  637. }
  638. if (g_pSRDlg->m_fDontDelete)
  639. {
  640. break;
  641. }
  642. ISpObjectToken *pToken = (ISpObjectToken*)(((NMLISTVIEW*)lParam)->lParam);
  643. if (pToken)
  644. {
  645. pToken->Release();
  646. }
  647. break;
  648. }
  649. case LVN_ITEMCHANGED:
  650. {
  651. // Code ends up here when a profile is added, deleted, or changed
  652. // We verify that we weren't selected before, but are now
  653. // and then kill the current reco context, deactivate the engine, change the profile
  654. // and fire everything back up again
  655. if ( IDC_USER == wParam )
  656. {
  657. LPNMLISTVIEW lplv = (LPNMLISTVIEW) lParam;
  658. if ( !(lplv->uOldState & LVIS_FOCUSED) && lplv->uNewState & LVIS_FOCUSED )
  659. {
  660. if ( g_pSRDlg->m_cpRecoEngine && g_pSRDlg->m_cpRecoCtxt )
  661. {
  662. HRESULT hr;
  663. ISpObjectToken *pSelectedToken = (ISpObjectToken *) lplv->lParam;
  664. hr = g_pSRDlg->m_cpRecoEngine->SetRecoState( SPRST_INACTIVE );
  665. if ( SUCCEEDED( hr ) )
  666. {
  667. hr = g_pSRDlg->m_cpRecoEngine->SetRecoProfile( pSelectedToken );
  668. // Restart audio regardless of success of SetRecoProfile
  669. g_pSRDlg->m_cpRecoEngine->SetRecoState(SPRST_ACTIVE_ALWAYS);
  670. if ( FAILED( hr ) )
  671. {
  672. WCHAR szError[256];
  673. szError[0] = '\0';
  674. LoadString(_Module.GetResourceInstance(), IDS_PROFILE_WARNING, szError, sizeof(szError));
  675. MessageBox(g_pSRDlg->m_hDlg, szError, g_pSRDlg->m_szCaption, MB_ICONWARNING|g_dwIsRTLLayout);
  676. }
  677. }
  678. if ( SUCCEEDED( hr ) )
  679. {
  680. // This is now the new default
  681. g_pSRDlg->m_pCurUserToken = pSelectedToken;
  682. g_pSRDlg->UserSelChange( lplv->iItem );
  683. }
  684. }
  685. }
  686. }
  687. break;
  688. }
  689. case PSN_APPLY:
  690. {
  691. g_pSRDlg->OnApply();
  692. break;
  693. }
  694. case PSN_QUERYCANCEL: // user clicks the Cancel button
  695. {
  696. g_pSRDlg->OnCancel();
  697. break;
  698. }
  699. }
  700. break;
  701. case WM_COMMAND:
  702. if (CBN_SELCHANGE == HIWORD(wParam))
  703. {
  704. g_pSRDlg->EngineSelChange();
  705. }
  706. else if (HIWORD(wParam) == BN_CLICKED)
  707. {
  708. HRESULT hr = S_OK;
  709. if (LOWORD(wParam) == IDC_MODIFY) // the "Modify" button
  710. {
  711. hr = g_pSRDlg->TrySwitchDefaultEngine( true );
  712. if ( SUCCEEDED( hr ) )
  713. {
  714. g_pSRDlg->ProfileProperties();
  715. }
  716. // Switch back to original default, complaining about errors only if there
  717. // wasn't a complaint from the call to TrySwitchDefaultEngine
  718. hr = g_pSRDlg->ResetDefaultEngine( SUCCEEDED( hr ));
  719. }
  720. else if (LOWORD(wParam) == IDC_ADD) // the "Add" button
  721. {
  722. // The engine we want to add this user for may not be the currently-
  723. // running engine. Try and switch it, and complain if there's
  724. // a problem
  725. hr = g_pSRDlg->TrySwitchDefaultEngine( true );
  726. if ( SUCCEEDED( hr ) )
  727. {
  728. g_pSRDlg->CreateNewUser();
  729. }
  730. // Switch back to original default, but complain about errors
  731. // only if the UI actually succeeded in showing
  732. g_pSRDlg->ResetDefaultEngine( SUCCEEDED( hr ) );
  733. }
  734. else if (LOWORD(wParam) == IDC_DELETE) // the "Delete" button
  735. {
  736. g_pSRDlg->DeleteCurrentUser();
  737. }
  738. else if (LOWORD(wParam) == IDC_SR_ADV)
  739. {
  740. // The engine we want to display UI for may not be the currently
  741. // running engine. Try and switch it
  742. hr = g_pSRDlg->TrySwitchDefaultEngine( true );
  743. if(SUCCEEDED(hr))
  744. {
  745. // display the UI w/ the new temporary default
  746. g_pSRDlg->m_pCurRecoToken->DisplayUI(hWnd, NULL,
  747. SPDUI_EngineProperties, NULL, 0, g_pSRDlg->m_cpRecoEngine);
  748. }
  749. // Switch back to original default, complaining about errors only if there
  750. // wasn't a complaint from the call to TrySwitchDefaultEngine
  751. hr = g_pSRDlg->ResetDefaultEngine( SUCCEEDED( hr ));
  752. }
  753. else if(LOWORD(wParam) == IDC_USERTRAINING)
  754. {
  755. // The engine we want to display UI for may not be the currently
  756. // running engine. Try and switch it
  757. hr = g_pSRDlg->TrySwitchDefaultEngine( true );
  758. if(SUCCEEDED(hr))
  759. {
  760. // display the UI w/ the new temporary default
  761. SPDBG_ASSERT( g_pSRDlg->m_cpRecoEngine );
  762. g_pSRDlg->m_cpRecoEngine->DisplayUI(hWnd, NULL, SPDUI_UserTraining, NULL, 0);
  763. }
  764. // Switch back to original default, complaining about errors only if there
  765. // wasn't a complaint from the call to TrySwitchDefaultEngine
  766. hr = g_pSRDlg->ResetDefaultEngine( SUCCEEDED( hr ));
  767. }
  768. else if(LOWORD(wParam) == IDC_MICWIZ)
  769. {
  770. // The engine we want to display UI for may not be the currently
  771. // running engine. Try and switch it
  772. hr = g_pSRDlg->TrySwitchDefaultEngine( true );
  773. if(SUCCEEDED(hr))
  774. {
  775. // display the UI w/ the new temporary default
  776. SPDBG_ASSERT( g_pSRDlg->m_cpRecoEngine );
  777. g_pSRDlg->m_cpRecoEngine->DisplayUI(hWnd, NULL, SPDUI_MicTraining, NULL, 0);
  778. }
  779. // Switch back to original default, complaining about errors only if there
  780. // wasn't a complaint from the call to TrySwitchDefaultEngine
  781. hr = g_pSRDlg->ResetDefaultEngine( SUCCEEDED( hr ));
  782. }
  783. else if (LOWORD(wParam) == IDC_AUD_IN)
  784. {
  785. // The m_pAudioDlg will be non-NULL only if the audio dialog
  786. // has been previously brough up.
  787. // Otherwise, we need a newly-initialized one
  788. if ( !g_pSRDlg->m_pAudioDlg )
  789. {
  790. g_pSRDlg->m_pAudioDlg = new CAudioDlg( eINPUT );
  791. }
  792. ::DialogBoxParam( _Module.GetResourceInstance(),
  793. MAKEINTRESOURCE( IDD_AUDIO_DEFAULT ),
  794. hWnd,
  795. (DLGPROC) AudioDlgProc,
  796. (LPARAM) g_pSRDlg->m_pAudioDlg );
  797. if ( g_pSRDlg->m_pAudioDlg->IsAudioDeviceChangedSinceLastTime() )
  798. {
  799. // Warn the user that he needs to apply the changes
  800. WCHAR szWarning[MAX_LOADSTRING];
  801. szWarning[0] = 0;
  802. LoadString( _Module.GetResourceInstance(), IDS_AUDIOIN_CHANGE_WARNING, szWarning, MAX_LOADSTRING);
  803. MessageBox( g_pSRDlg->GetHDlg(), szWarning, g_pSRDlg->m_szCaption, MB_ICONWARNING |g_dwIsRTLLayout);
  804. }
  805. // Kick the Apply button
  806. g_pSRDlg->KickCPLUI();
  807. }
  808. }
  809. break;
  810. }
  811. return FALSE;
  812. } /* SRDlgProc */
  813. /****************************************************************************
  814. * CSRDlg::CreateNewUser *
  815. *-------------------------*
  816. * Description: Adds a new speech user profile to the registry
  817. *
  818. * Returns:
  819. *
  820. ********************************************************************* RAL ***/
  821. void CSRDlg::CreateNewUser()
  822. {
  823. SPDBG_FUNC("CSRDlg::CreateNewUser");
  824. HRESULT hr = S_OK;
  825. // Make sure that we haven't already added too many profiles to keep track of
  826. if ( m_iAddedTokens >= iMaxAddedProfiles_c )
  827. {
  828. WCHAR wszError[ MAX_LOADSTRING ];
  829. ::LoadString( _Module.GetResourceInstance(), IDS_MAX_PROFILES_EXCEEDED,
  830. wszError, MAX_LOADSTRING );
  831. ::MessageBox( m_hDlg, wszError, m_szCaption, MB_ICONEXCLAMATION | g_dwIsRTLLayout );
  832. return;
  833. }
  834. CComPtr<ISpObjectToken> cpNewToken;
  835. hr = SpCreateNewToken(SPCAT_RECOPROFILES, NULL, &cpNewToken);
  836. if (SUCCEEDED(hr))
  837. {
  838. if (!UserPropDlg(cpNewToken)) // User canceled!
  839. {
  840. cpNewToken->Remove(NULL);
  841. }
  842. else
  843. {
  844. //set the default
  845. m_pCurUserToken = cpNewToken;
  846. // Put the new token on the added tokens list
  847. cpNewToken->GetId( &(m_aAddedTokens[ m_iAddedTokens++ ]) );
  848. // make this the default after we edit it
  849. ChangeDefaultUser();
  850. // This will make sure that it gets displayed.
  851. // Note that m_pCurUserToken will point an AddRefed ISpObjectToken *
  852. // after the call to PopulateList()
  853. PopulateList();
  854. // Update the UI
  855. KickCPLUI();
  856. }
  857. }
  858. else
  859. {
  860. WCHAR szError[MAX_LOADSTRING];
  861. szError[0] = 0;
  862. LoadString(_Module.GetResourceInstance(), IDS_RECOPROFILE_ADD_ERROR, szError, MAX_LOADSTRING);
  863. MessageBox( m_hDlg, szError, m_szCaption, MB_ICONWARNING | g_dwIsRTLLayout);
  864. }
  865. // Only enable the delete button if there are 2 or more user profiles
  866. int iNumUsers = (int)::SendMessage(m_hUserList, LVM_GETITEMCOUNT, 0, 0);
  867. if (iNumUsers < 2)
  868. {
  869. EnableWindow(GetDlgItem(m_hDlg, IDC_DELETE), FALSE);
  870. }
  871. else
  872. {
  873. EnableWindow(GetDlgItem(m_hDlg, IDC_DELETE), TRUE);
  874. }
  875. // Sort the items initially
  876. ::SendMessage( m_hUserList, LVM_SORTITEMS, (LPARAM)m_pCurUserToken, LPARAM(&SortCols) );
  877. }
  878. /****************************************************************************
  879. * CSRDlg::UserPropDlg *
  880. *-----------------------*
  881. * Description: This is for when a user wants to add a new profile
  882. *
  883. * Returns:
  884. *
  885. ********************************************************************* BRENTMID ***/
  886. HRESULT CSRDlg::UserPropDlg(ISpObjectToken * pToken)
  887. {
  888. SPDBG_FUNC("CSRDlg::UserPropDlg");
  889. HRESULT hr = S_OK;
  890. CEnvrPropDlg Dlg(this, pToken);
  891. hr = (HRESULT)DialogBoxParam(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDD_PROF_WIZ), m_hDlg,
  892. CEnvrPropDlg::DialogProc, (LPARAM)(&Dlg));
  893. return hr;
  894. }
  895. /****************************************************************************
  896. * CEnvrPropDlg::InitDialog *
  897. *--------------------------*
  898. * Description:
  899. *
  900. * Returns:
  901. *
  902. ********************************************************************* RAL ***/
  903. BOOL CEnvrPropDlg::InitDialog(HWND hDlg)
  904. {
  905. USES_CONVERSION;
  906. CSpDynamicString dstrDescription;
  907. m_hDlg = hDlg;
  908. //
  909. // Get the description if there is one...
  910. //
  911. SpGetDescription(m_cpToken, &dstrDescription);
  912. if (dstrDescription)
  913. {
  914. ::SendDlgItemMessage(hDlg, IDC_USER_NAME, WM_SETTEXT, 0, (LPARAM) dstrDescription.m_psz);
  915. ::SendDlgItemMessage(hDlg, IDC_USER_NAME, EM_LIMITTEXT, UNLEN, 0);
  916. }
  917. // We want the EN_CHANGE notifications from the edit control
  918. ::SendDlgItemMessage( hDlg, IDC_USER_NAME, EM_SETEVENTMASK, 0, ENM_CHANGE );
  919. if (!m_isModify)
  920. {
  921. // Set the user name to the one in the registry, if found;
  922. // otherwise set it to the user name
  923. HKEY hkUserKey;
  924. LONG lUserOpen;
  925. WCHAR szUserName[ UNLEN + 1 ];
  926. szUserName[0] = 0;
  927. DWORD dwUserLen = UNLEN + 1;
  928. lUserOpen = ::RegOpenKeyEx( HKEY_CURRENT_USER,
  929. L"Software\\Microsoft\\MS Setup (ACME)\\User Info",
  930. 0, KEY_READ, &hkUserKey );
  931. if ( lUserOpen == ERROR_SUCCESS )
  932. {
  933. lUserOpen = RegQueryValueEx( hkUserKey, L"DefName", NULL, NULL,
  934. (BYTE *) szUserName, &dwUserLen );
  935. RegCloseKey(hkUserKey);
  936. }
  937. if ( ERROR_SUCCESS != lUserOpen )
  938. {
  939. // Just use the win32 user name
  940. BOOL fSuccess = ::GetUserName( szUserName, &dwUserLen );
  941. if ( !fSuccess )
  942. {
  943. szUserName[0] = 0;
  944. }
  945. }
  946. // Now put that in the edit box.
  947. // First check to make sure the name is nonempty
  948. // and enable the UI accordingly
  949. WCHAR *pwch;
  950. for ( pwch = szUserName; *pwch && iswspace( *pwch ); pwch++ )
  951. {
  952. }
  953. ::EnableWindow( ::GetDlgItem( m_hDlg, IDOK ), (0 != *pwch) );
  954. ::EnableWindow( ::GetDlgItem( m_hDlg, ID_NEXT ), (0 != *pwch) );
  955. // Set the edit box to have the user's name
  956. // Need to use SETTEXTEX since this might contain wide chars
  957. SETTEXTEX stx;
  958. stx.flags = ST_DEFAULT;
  959. stx.codepage = 1200;
  960. ::SendDlgItemMessage( m_hDlg,
  961. IDC_USER_NAME, EM_SETTEXTEX, (WPARAM) &stx, (LPARAM) szUserName );
  962. }
  963. ::SetFocus(::GetDlgItem(hDlg, IDC_USER_NAME));
  964. return TRUE;
  965. }
  966. /****************************************************************************
  967. * CEnvrPropDlg::ApplyChanges *
  968. *----------------------------*
  969. * Description:
  970. *
  971. * Returns:
  972. *
  973. ********************************************************************* RAL ***/
  974. EPD_RETURN_VALUE CEnvrPropDlg::ApplyChanges()
  975. {
  976. USES_CONVERSION;
  977. SPDBG_FUNC("CEnvrPropDlg::ApplyChanges");
  978. WCHAR szName[UNLEN + 1];
  979. *szName = 0;
  980. GETTEXTEX gtex = { sp_countof(szName), GT_DEFAULT, 1200, NULL, NULL };
  981. ::SendDlgItemMessage(m_hDlg, IDC_USER_NAME, EM_GETTEXTEX, (WPARAM)&gtex, (LPARAM)szName);
  982. if (*szName == 0)
  983. {
  984. return EPD_FAILED;
  985. }
  986. // Check to see if this profile name already exists
  987. CComPtr<IEnumSpObjectTokens> cpEnum;
  988. ISpObjectToken *pToken;
  989. CSpDynamicString dstrDescription;
  990. CSpDynamicString dInputString;
  991. CSpDynamicString dstrOldTok;
  992. bool isDuplicate = false;
  993. HRESULT hr = SpEnumTokens(SPCAT_RECOPROFILES, NULL, NULL, &cpEnum);
  994. // Get the description of the currently selected profile
  995. dstrOldTok.Clear();
  996. hr = SpGetDescription( m_pParent->m_pCurUserToken, &dstrOldTok );
  997. while (cpEnum && cpEnum->Next(1, &pToken, NULL) == S_OK)
  998. {
  999. // Get the description of the enumerated token
  1000. dstrDescription.Clear();
  1001. hr = SpGetDescription( pToken, &dstrDescription );
  1002. pToken->Release();
  1003. // Get the input string
  1004. dInputString = szName;
  1005. if ( SUCCEEDED(hr) )
  1006. {
  1007. if ( wcscmp( dstrDescription.m_psz, dInputString.m_psz ) == 0 )
  1008. {
  1009. // the name is duplicated
  1010. isDuplicate = true;
  1011. }
  1012. }
  1013. }
  1014. if ( isDuplicate ) // this not a modify box and the user entered a duplicate name
  1015. {
  1016. return EPD_DUP; // tell the user about it
  1017. }
  1018. if (FAILED(SpSetDescription(m_cpToken, szName)))
  1019. {
  1020. return EPD_FAILED;
  1021. }
  1022. return EPD_OK;
  1023. }
  1024. /*****************************************************************************
  1025. * EnvrPropDialogProc *
  1026. *--------------------*
  1027. * Description:
  1028. * Mesage handler for User Name dialog
  1029. ****************************************************************** BRENTMID ***/
  1030. INT_PTR CALLBACK CEnvrPropDlg::DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
  1031. {
  1032. USES_CONVERSION;
  1033. static CSpUnicodeSupport unicode;
  1034. CEnvrPropDlg * pThis = (CEnvrPropDlg *) unicode.GetWindowLongPtr(hDlg, GWLP_USERDATA);
  1035. switch (message)
  1036. {
  1037. case WM_INITDIALOG:
  1038. unicode.SetWindowLongPtr(hDlg, GWLP_USERDATA, lParam);
  1039. pThis = (CEnvrPropDlg *)lParam;
  1040. return pThis->InitDialog(hDlg);
  1041. case WM_COMMAND:
  1042. {
  1043. if (( IDC_USER_NAME == LOWORD(wParam) )
  1044. && ( EN_CHANGE == HIWORD(wParam) ))
  1045. {
  1046. // Edit control contents have changed:
  1047. // See if we should enable the "finish" and "next" buttons by getting the
  1048. // text in the edit box and making sure it has at least one
  1049. // non-whitespace character
  1050. WCHAR szName[ UNLEN+1 ];
  1051. *szName = 0;
  1052. GETTEXTEX gtex = { UNLEN, GT_DEFAULT, 1200, NULL, NULL };
  1053. ::SendDlgItemMessage(pThis->m_hDlg,
  1054. IDC_USER_NAME, EM_GETTEXTEX, (WPARAM)&gtex, (LPARAM)szName);
  1055. WCHAR *pch = szName;
  1056. for ( ; *pch && iswspace( *pch ); pch++ )
  1057. {
  1058. }
  1059. ::EnableWindow( ::GetDlgItem( pThis->m_hDlg, IDOK ), (0 != *pch) );
  1060. ::EnableWindow( ::GetDlgItem( pThis->m_hDlg, ID_NEXT ), (0 != *pch) );
  1061. break;
  1062. }
  1063. if( LOWORD(wParam) == IDCANCEL )
  1064. {
  1065. EndDialog(hDlg, FALSE);
  1066. return TRUE;
  1067. }
  1068. // user clicks the NEXT button
  1069. if ( (LOWORD( wParam ) == ID_NEXT) || (LOWORD( wParam ) == IDOK) )
  1070. {
  1071. EPD_RETURN_VALUE eRet = pThis->ApplyChanges();
  1072. if ( eRet == EPD_OK )
  1073. {
  1074. if ( ID_NEXT == LOWORD(wParam) )
  1075. {
  1076. // Launch the micwiz, if we can
  1077. // Try to switch engines in case the user has changed engines
  1078. // without applying
  1079. HRESULT hr = g_pSRDlg->TrySwitchDefaultEngine( true );
  1080. if ( S_OK == hr )
  1081. {
  1082. SPDBG_ASSERT( g_pSRDlg->m_cpRecoEngine );
  1083. if ( g_pSRDlg->m_cpRecoEngine )
  1084. {
  1085. // Switch the recoprofile to the new one (might need to turn off
  1086. // recostate first
  1087. // Turn off recostate before calling SetRecoProfile() if necessary
  1088. SPRECOSTATE eOldRecoState = SPRST_INACTIVE;
  1089. g_pSRDlg->m_cpRecoEngine->GetRecoState( &eOldRecoState );
  1090. HRESULT hrRecoState = S_OK;
  1091. if ( SPRST_INACTIVE != eOldRecoState )
  1092. {
  1093. hrRecoState = g_pSRDlg->m_cpRecoEngine->SetRecoState( SPRST_INACTIVE );
  1094. }
  1095. // Change to the newly-added recoprofile
  1096. HRESULT hrSetRecoProfile = E_FAIL;
  1097. if ( SUCCEEDED( hrRecoState ) )
  1098. {
  1099. hrSetRecoProfile =
  1100. g_pSRDlg->m_cpRecoEngine->SetRecoProfile( pThis->m_cpToken );
  1101. // Restore the recostate
  1102. g_pSRDlg->m_cpRecoEngine->SetRecoState( eOldRecoState );
  1103. }
  1104. // Bring on the micwiz and the training wiz
  1105. // Follow the yellow brick road.
  1106. g_pSRDlg->m_cpRecoEngine->DisplayUI(hDlg, NULL, SPDUI_MicTraining, NULL, 0);
  1107. if ( SUCCEEDED( hrSetRecoProfile ) )
  1108. {
  1109. // Only want to train the profile if it actually _is_ this profile being
  1110. // used...
  1111. g_pSRDlg->m_cpRecoEngine->DisplayUI(hDlg, NULL, SPDUI_UserTraining, NULL, 0);
  1112. }
  1113. }
  1114. }
  1115. // Switch back to original default, complaining about errors only if there
  1116. // wasn't a complaint from the call to TrySwitchDefaultEngine
  1117. hr = g_pSRDlg->ResetDefaultEngine( SUCCEEDED( hr ));
  1118. }
  1119. // now we are done
  1120. EndDialog(hDlg, TRUE);
  1121. }
  1122. else if ( eRet == EPD_DUP ) // user tried to enter a duplicate name
  1123. {
  1124. // What name was added?
  1125. WCHAR szName[ UNLEN+1 ];
  1126. *szName = 0;
  1127. GETTEXTEX gtex = { UNLEN, GT_DEFAULT, 1200, NULL, NULL };
  1128. ::SendDlgItemMessage(pThis->m_hDlg,
  1129. IDC_USER_NAME, EM_GETTEXTEX, (WPARAM)&gtex, (LPARAM)szName);
  1130. WCHAR pszDuplicate[MAX_LOADSTRING];
  1131. LoadString(_Module.GetResourceInstance(),
  1132. g_pSRDlg->IsProfileNameInvisible( szName ) ? IDS_DUP_NAME_DELETED : IDS_DUP_NAME,
  1133. pszDuplicate, MAX_LOADSTRING);
  1134. MessageBox( hDlg, pszDuplicate, g_pSRDlg->m_szCaption, MB_ICONEXCLAMATION | g_dwIsRTLLayout );
  1135. }
  1136. }
  1137. }
  1138. break;
  1139. }
  1140. return FALSE;
  1141. } /* UserNameDialogProc */
  1142. /*****************************************************************************
  1143. * CSRDlg::UserSelChange *
  1144. *-------------------------*
  1145. * Description:
  1146. * Changes the deafult user
  1147. ****************************************************************** BRENTMID ***/
  1148. void CSRDlg::UserSelChange( int iSelIndex )
  1149. {
  1150. HRESULT hr = S_OK;
  1151. SPDBG_FUNC( "CSRDlg::UserSelChange" );
  1152. // Get the selected item's token
  1153. LVITEM lvitem;
  1154. lvitem.iItem = iSelIndex;
  1155. lvitem.iSubItem = 0;
  1156. lvitem.mask = LVIF_PARAM;
  1157. ::SendMessage( m_hUserList, LVM_GETITEM, 0, (LPARAM) &lvitem );
  1158. ISpObjectToken *pToken = (ISpObjectToken *) lvitem.lParam;
  1159. if (pToken)
  1160. {
  1161. // Try to find the item in the list associated with the current default token
  1162. LVFINDINFO lvfi;
  1163. if ( iSelIndex >= 0 )
  1164. {
  1165. // Something was selected; this is the new default user
  1166. lvfi.flags = LVFI_PARAM;
  1167. lvfi.lParam = (LPARAM) m_pCurUserToken;
  1168. int iCurDefaultIndex = (int)::SendMessage( m_hUserList, LVM_FINDITEM, -1, (LPARAM) &lvfi );
  1169. if ( iCurDefaultIndex >= 0 )
  1170. {
  1171. // The current default has been found in the list; remove its checkmark
  1172. SetCheckmark( m_hUserList, iCurDefaultIndex, false );
  1173. }
  1174. SetCheckmark( m_hUserList, iSelIndex, true );
  1175. //set the default
  1176. m_pCurUserToken = pToken;
  1177. m_iLastSelected = iSelIndex;
  1178. // Kick the Apply button
  1179. KickCPLUI();
  1180. }
  1181. }
  1182. } /* CSRDlg::UserSelChange */
  1183. /*****************************************************************************
  1184. * CSRDlg::DeleteCurrentUser *
  1185. *-------------------------*
  1186. * Description:
  1187. * Deletes the default user
  1188. ****************************************************************** BRENTMID ***/
  1189. void CSRDlg::DeleteCurrentUser()
  1190. {
  1191. // Make sure that we haven't already deleted too many profiles to keep track of
  1192. if ( m_iDeletedTokens >= iMaxDeletedProfiles_c )
  1193. {
  1194. WCHAR wszError[ MAX_LOADSTRING ];
  1195. ::LoadString( _Module.GetResourceInstance(), IDS_MAX_PROFILES_EXCEEDED,
  1196. wszError, MAX_LOADSTRING );
  1197. ::MessageBox( m_hDlg, wszError, m_szCaption, MB_ICONEXCLAMATION | g_dwIsRTLLayout );
  1198. return;
  1199. }
  1200. // First confirm this action with the user
  1201. WCHAR pszAsk[ MAX_LOADSTRING ];
  1202. WCHAR pszWinTitle[ MAX_LOADSTRING ];
  1203. ::LoadString( _Module.GetResourceInstance(), IDS_ASK_CONFIRM, pszAsk, MAX_LOADSTRING );
  1204. ::LoadString( _Module.GetResourceInstance(), IDS_ASK_TITLE, pszWinTitle, MAX_LOADSTRING );
  1205. if ( MessageBox( m_hDlg, pszAsk, pszWinTitle, MB_YESNO | g_dwIsRTLLayout ) == IDNO )
  1206. {
  1207. // User said no.
  1208. return;
  1209. }
  1210. // We need to hang onto the current user token, since when the focus
  1211. // changes because of the delete, there will be a different m_pCurUserToken
  1212. ISpObjectToken *pTokenToDelete = m_pCurUserToken;
  1213. SPDBG_ASSERT( pTokenToDelete );
  1214. if ( !pTokenToDelete )
  1215. {
  1216. return;
  1217. }
  1218. m_fDontDelete = TRUE;
  1219. // Try to find the item in the list associated with the current default token
  1220. LVFINDINFO lvfi;
  1221. lvfi.flags = LVFI_PARAM;
  1222. lvfi.lParam = (LPARAM) pTokenToDelete;
  1223. int iCurDefaultIndex = (int)::SendMessage( m_hUserList, LVM_FINDITEM, -1, (LPARAM) &lvfi );
  1224. if ( iCurDefaultIndex >= 0 )
  1225. {
  1226. // The current default has been found in the list; remove its checkmark
  1227. SetCheckmark( m_hUserList, iCurDefaultIndex, false );
  1228. }
  1229. //remove the token
  1230. ::SendMessage( m_hUserList, LVM_DELETEITEM, iCurDefaultIndex, NULL );
  1231. // now setup the new default
  1232. // Get the first item's token
  1233. LVITEM lvitem;
  1234. lvitem.iItem = 0;
  1235. lvitem.iSubItem = 0;
  1236. lvitem.mask = LVIF_PARAM;
  1237. ::SendMessage( m_hUserList, LVM_GETITEM, 0, (LPARAM) &lvitem );
  1238. ISpObjectToken *pToken = (ISpObjectToken *) lvitem.lParam;
  1239. // set the selected item.
  1240. // Focusing it will cause it to be the default
  1241. lvitem.state = LVIS_SELECTED | LVIS_FOCUSED;
  1242. lvitem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
  1243. ::SendMessage( m_hUserList, LVM_SETITEMSTATE, 0, (LPARAM) &lvitem );
  1244. SetCheckmark( m_hUserList, 0, true );
  1245. // enable or disable the delete button based on # of profiles
  1246. int iNumUsers = (int)::SendMessage(m_hUserList, LVM_GETITEMCOUNT, 0, 0);
  1247. if (iNumUsers < 2)
  1248. {
  1249. EnableWindow(GetDlgItem(m_hDlg, IDC_DELETE), FALSE);
  1250. }
  1251. else
  1252. {
  1253. EnableWindow(GetDlgItem(m_hDlg, IDC_DELETE), TRUE);
  1254. }
  1255. //set the focus back to the user profiles
  1256. ::SetFocus(GetDlgItem( m_hDlg, IDC_USER ));
  1257. // set the new default profile, inform the SR engine, and remove the old token
  1258. SpSetDefaultTokenForCategoryId(SPCAT_RECOPROFILES, m_pCurUserToken );
  1259. // Save the tokens in case the user clicks "Cancel"
  1260. m_aDeletedTokens[m_iDeletedTokens] = pTokenToDelete; // save the deleted token for possible "Cancel"
  1261. m_iDeletedTokens++; // increment the number deleted
  1262. KickCPLUI();
  1263. // Currently we immediately APPLY this deletion, since the user has already said "YES"
  1264. // when the were prompted to confirm the delete.
  1265. // If we want to have an "APPLY / CANCEL" thing happen, switch the #if 1 and #if 0
  1266. // send the appropriate message to the parent
  1267. HWND parentWin = ::GetParent( m_hDlg );
  1268. // now the last selected token is gone, so note that
  1269. m_iLastSelected = -1;
  1270. // Sort the items initially
  1271. ::SendMessage( m_hUserList, LVM_SORTITEMS, (LPARAM)m_pCurUserToken, LPARAM(&SortCols) );
  1272. m_fDontDelete = FALSE;
  1273. } /* CSRDlg::DeleteCurrentUser */
  1274. /*****************************************************************************
  1275. * CSRDlg::ProfileProperties *
  1276. *-------------------------*
  1277. * Description:
  1278. * Modifies the properites through engine UI
  1279. ****************************************************************** BRENTMID ***/
  1280. void CSRDlg::ProfileProperties()
  1281. {
  1282. if ( m_cpRecoEngine )
  1283. {
  1284. m_cpRecoEngine->DisplayUI(m_hDlg, NULL, SPDUI_RecoProfileProperties, NULL, 0);
  1285. }
  1286. }
  1287. /*****************************************************************************
  1288. * CSRDlg::OnInitDialog *
  1289. *----------------------*
  1290. * Description:
  1291. * Dialog Initialization
  1292. ****************************************************************** MIKEAR ***/
  1293. void CSRDlg::OnInitDialog(HWND hWnd)
  1294. {
  1295. SPDBG_FUNC( "CSRDlg::OnInitDialog" );
  1296. USES_CONVERSION;
  1297. SPDBG_ASSERT(IsWindow(hWnd));
  1298. m_hDlg = hWnd;
  1299. // This will be the caption for all MessageBoxes
  1300. m_szCaption[0] = 0;
  1301. ::LoadString( _Module.GetResourceInstance(), IDS_CAPTION, m_szCaption, sp_countof( m_szCaption ) );
  1302. m_hSRCombo = ::GetDlgItem( hWnd, IDC_COMBO_RECOGNIZERS );
  1303. SpInitTokenComboBox( m_hSRCombo, SPCAT_RECOGNIZERS );
  1304. // The first one in the list will be the current default
  1305. int iSelected = (int) ::SendMessage( m_hSRCombo, CB_GETCURSEL, 0, 0 );
  1306. ISpObjectToken *pCurDefault = (ISpObjectToken *) ::SendMessage( m_hSRCombo, CB_GETITEMDATA, iSelected, 0 );
  1307. m_pCurRecoToken = pCurDefault;
  1308. m_pDefaultRecToken = pCurDefault;
  1309. // This simulates selecting the default engine - ensures the UI is setup correctly.
  1310. EngineSelChange(TRUE);
  1311. InitUserList( hWnd );
  1312. m_hUserList = ::GetDlgItem( hWnd, IDC_USER );
  1313. ::SendMessage( m_hUserList, LVM_SETCOLUMNWIDTH, 0, MAKELPARAM((int) LVSCW_AUTOSIZE, 0) );
  1314. int iNumUsers = (int)::SendMessage(m_hUserList, LVM_GETITEMCOUNT, 0, 0);
  1315. if (iNumUsers < 2)
  1316. {
  1317. EnableWindow(GetDlgItem(m_hDlg, IDC_DELETE), FALSE);
  1318. }
  1319. else
  1320. {
  1321. EnableWindow(GetDlgItem(m_hDlg, IDC_DELETE), TRUE);
  1322. }
  1323. //set the focus back to the user profiles
  1324. ::SetFocus(GetDlgItem( m_hDlg, IDC_USER ));
  1325. } /* CSRDlg::OnInitDialog */
  1326. /*****************************************************************************
  1327. * CSRDlg::SetCheckmark *
  1328. *----------------------*
  1329. * Description:
  1330. * Sets the specified item in the list control to be either checked
  1331. * or unchecked (as the default user)
  1332. ******************************************************************************/
  1333. void CSRDlg::SetCheckmark( HWND hList, int iIndex, bool bCheck )
  1334. {
  1335. ListView_SetCheckState( hList, iIndex, bCheck );
  1336. } /* CSRDlg::SetCheckmark */
  1337. /*****************************************************************************
  1338. * CSRDlg::OnDestroy *
  1339. *-------------------*
  1340. * Description:
  1341. * Destruction
  1342. ****************************************************************** MIKEAR ***/
  1343. void CSRDlg::OnDestroy()
  1344. {
  1345. SPDBG_FUNC( "CSRDlg::OnDestroy" );
  1346. // spuihelp will take care of releasing its own tokens
  1347. SpDestroyTokenComboBox( m_hSRCombo );
  1348. // The tokens kepts as itemdata in the reco profile list were
  1349. // released in the LVN_DELETEITEM code
  1350. // Shuts off the reco engine
  1351. ShutDown();
  1352. } /* CSRDlg::OnDestroy */
  1353. /*****************************************************************************
  1354. * CSRDlg::ShutDown *
  1355. *------------------*
  1356. * Description:
  1357. * Shuts down by releasing the engine and reco context
  1358. ****************************************************************** MIKEAR ***/
  1359. void CSRDlg::ShutDown()
  1360. {
  1361. // Release objects
  1362. m_cpRecoCtxt.Release();
  1363. m_cpRecoEngine.Release();
  1364. } /* CSRDlg::ShutDown */
  1365. /************************************************************
  1366. * CSRDlg::InitUserList
  1367. *
  1368. * Description:
  1369. * Initializes user list
  1370. *********************************************** BRENTMID ***/
  1371. void CSRDlg::InitUserList(HWND hWnd)
  1372. {
  1373. const int iInitWidth_c = 260; // pixel width of "Description Column"
  1374. // Set up the "Description" column for the settings display
  1375. m_hUserList = ::GetDlgItem( hWnd, IDC_USER );
  1376. WCHAR pszColumnText[ UNLEN+1 ] = L"";
  1377. LVCOLUMN lvc;
  1378. lvc.mask = LVCF_FMT| LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
  1379. ::LoadString( _Module.GetResourceInstance(), IDS_DESCRIPT, pszColumnText, UNLEN );
  1380. lvc.pszText = pszColumnText;
  1381. lvc.iSubItem = 0;
  1382. lvc.cx = iInitWidth_c;
  1383. lvc.fmt = LVCFMT_LEFT;
  1384. ListView_InsertColumn( m_hUserList, 1, &lvc );
  1385. // This should be a checkbox list
  1386. ::SendMessage( m_hUserList, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES );
  1387. PopulateList();
  1388. // Sort the items initially
  1389. ::SendMessage( m_hUserList, LVM_SORTITEMS, (LPARAM)m_pCurUserToken, LPARAM(&SortCols) );
  1390. } // CSRDlg::InitUserList
  1391. /************************************************************
  1392. * CSRDlg::PopulateList
  1393. *
  1394. * Description:
  1395. * Populates user list
  1396. *********************************************** BRENTMID ***/
  1397. void CSRDlg::PopulateList()
  1398. {
  1399. USES_CONVERSION;
  1400. // Populate the list control
  1401. int iIndex = 0;
  1402. LVITEM lvitem;
  1403. CComPtr<IEnumSpObjectTokens> cpEnum;
  1404. ISpObjectToken *pToken;
  1405. WCHAR *pszAttrib = NULL;
  1406. HRESULT hr;
  1407. // this is to lazily init the user profile if there are none - DON'T REMOVE
  1408. if ( m_cpRecoEngine )
  1409. {
  1410. CComPtr<ISpObjectToken> cpTempToken;
  1411. m_cpRecoEngine->GetRecoProfile(&cpTempToken);
  1412. }
  1413. // Now clear the list
  1414. ListView_DeleteAllItems( m_hUserList );
  1415. // We will list the tokens in the order they are enumerated
  1416. hr = SpEnumTokens(SPCAT_RECOPROFILES, NULL, NULL, &cpEnum);
  1417. if (hr == S_OK)
  1418. {
  1419. bool fSetDefault = false;
  1420. while (cpEnum->Next(1, &pToken, NULL) == S_OK)
  1421. {
  1422. // first check to see if the token is in the "Deleted List"
  1423. bool f_isDel = false;
  1424. for (int iDel = 0; iDel < m_iDeletedTokens; iDel++)
  1425. {
  1426. CSpDynamicString dstrT1;
  1427. CSpDynamicString dstrT2;
  1428. pToken->GetId( &dstrT1 );
  1429. m_aDeletedTokens[ iDel ]->GetId( &dstrT2 );
  1430. if (dstrT1.m_psz && dstrT2.m_psz && !wcscmp(dstrT1.m_psz, dstrT2.m_psz))
  1431. {
  1432. f_isDel = true;
  1433. }
  1434. }
  1435. // if we should show it
  1436. if ( f_isDel )
  1437. {
  1438. // This token has a refcounted reference to it on the deleted list:
  1439. // this reference should be released
  1440. pToken->Release();
  1441. }
  1442. else
  1443. {
  1444. // Not a pending delete: We should show it
  1445. // now insert the token
  1446. lvitem.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE;
  1447. lvitem.iItem = iIndex;
  1448. lvitem.iSubItem = 0;
  1449. lvitem.lParam = (LPARAM) pToken;
  1450. CSpDynamicString cUser;
  1451. SpGetDescription(pToken, &cUser);
  1452. lvitem.pszText = cUser;
  1453. // if this is the default it should be selected/focused
  1454. if ( !fSetDefault )
  1455. {
  1456. lvitem.state = LVIS_SELECTED | LVIS_FOCUSED;
  1457. }
  1458. else
  1459. {
  1460. lvitem.state = 0;
  1461. }
  1462. iIndex = (int)::SendMessage( m_hUserList, LVM_INSERTITEM, 0, (LPARAM) &lvitem );
  1463. // the default is the first token returned by cpEnum->Next
  1464. if ( !fSetDefault )
  1465. {
  1466. fSetDefault = true;
  1467. // Put the checkmark there
  1468. SetCheckmark( m_hUserList, iIndex, true );
  1469. m_pCurUserToken = pToken;
  1470. // Set the m_dstrOldUserTokenId to the first default if it hasn't been set yet.
  1471. if ( !m_dstrOldUserTokenId )
  1472. {
  1473. m_pCurUserToken->GetId( &m_dstrOldUserTokenId );
  1474. }
  1475. }
  1476. iIndex++;
  1477. }
  1478. }
  1479. // Autosize according to the strings now in the list
  1480. ::SendMessage( m_hUserList, LVM_SETCOLUMNWIDTH, 0, MAKELPARAM((int) LVSCW_AUTOSIZE, 0) );
  1481. }
  1482. // now find the default item so we can scroll to it
  1483. // Try to find the item in the list associated with the current default token
  1484. LVFINDINFO lvfi;
  1485. lvfi.flags = LVFI_PARAM;
  1486. lvfi.lParam = (LPARAM) m_pCurUserToken;
  1487. int iCurDefaultIndex = (int)::SendMessage( m_hUserList, LVM_FINDITEM, -1, (LPARAM) &lvfi );
  1488. if ( iCurDefaultIndex >= 0 )
  1489. {
  1490. // The current default has been found in the list; scroll to it
  1491. ListView_EnsureVisible( m_hUserList, iCurDefaultIndex, false );
  1492. }
  1493. // Name the list view something appropriate
  1494. WCHAR pszListName[ MAX_LOADSTRING ];
  1495. ::LoadString( _Module.GetResourceInstance(), IDS_PROFILE_LIST_NAME, pszListName, MAX_LOADSTRING );
  1496. ::SendMessage( m_hUserList, WM_SETTEXT, 0, (LPARAM)pszListName );
  1497. }
  1498. /*****************************************************************************
  1499. * CSRDlg::OnApply *
  1500. *-----------------*
  1501. * Description:
  1502. * Set user specified options
  1503. ****************************************************************** MIKEAR ***/
  1504. void CSRDlg::OnApply()
  1505. {
  1506. SPDBG_FUNC( "CSRDlg::OnApply" );
  1507. int iSelected = (int) ::SendMessage( m_hSRCombo, CB_GETCURSEL, 0, 0 );
  1508. ULONG ulFlags = 0;
  1509. // Pick up the recognizer change, if any
  1510. bool fRecognizerChange = false;
  1511. ISpObjectToken *pToken = NULL;
  1512. if ( HasRecognizerChanged() )
  1513. {
  1514. pToken = (ISpObjectToken *) ::SendMessage( m_hSRCombo, CB_GETITEMDATA, iSelected, 0 );
  1515. if ( CB_ERR == (LRESULT) pToken )
  1516. {
  1517. pToken = NULL;
  1518. }
  1519. HRESULT hrEngine = S_OK;
  1520. if (pToken && (iSelected >=0))
  1521. {
  1522. hrEngine = SpSetDefaultTokenForCategoryId(SPCAT_RECOGNIZERS, pToken );
  1523. if (FAILED(hrEngine))
  1524. {
  1525. WCHAR szError[256];
  1526. szError[0] = '\0';
  1527. LoadString(_Module.GetResourceInstance(), IDS_DEFAULT_ENGINE_WARNING, szError, sizeof(szError));
  1528. MessageBox(m_hDlg, szError, MB_OK, MB_ICONWARNING | g_dwIsRTLLayout);
  1529. }
  1530. else
  1531. {
  1532. fRecognizerChange = true;
  1533. }
  1534. }
  1535. }
  1536. // Pick up any audio changes that may have been made
  1537. HRESULT hrAudio = S_OK;
  1538. bool fAudioChange = false;
  1539. if ( m_pAudioDlg )
  1540. {
  1541. fAudioChange = m_pAudioDlg->IsAudioDeviceChanged();
  1542. if ( fAudioChange )
  1543. {
  1544. hrAudio = m_pAudioDlg->OnApply();
  1545. }
  1546. if ( FAILED( hrAudio ) )
  1547. {
  1548. WCHAR szError[256];
  1549. szError[0] = '\0';
  1550. LoadString(_Module.GetResourceInstance(), IDS_AUDIO_CHANGE_FAILED, szError, sizeof(szError));
  1551. MessageBox(m_hDlg, szError, NULL, MB_ICONWARNING|g_dwIsRTLLayout);
  1552. }
  1553. // Kill the audio dialog, as we are done with it.
  1554. delete m_pAudioDlg;
  1555. m_pAudioDlg = NULL;
  1556. }
  1557. // Permanently delete any profiles the user has deleted
  1558. for (int iIndex = 0; iIndex < m_iDeletedTokens; iIndex++)
  1559. {
  1560. HRESULT hr = m_aDeletedTokens[iIndex]->Remove(NULL);
  1561. if (FAILED(hr))
  1562. {
  1563. // might fail if a user has another app open
  1564. WCHAR szError[256];
  1565. szError[0] = '\0';
  1566. LoadString(_Module.GetResourceInstance(), IDS_REMOVE_WARNING, szError, sizeof(szError));
  1567. MessageBox(m_hDlg, szError, MB_OK, MB_ICONWARNING|g_dwIsRTLLayout);
  1568. // This will make sure that the attempted deleted item shows up again
  1569. PopulateList();
  1570. }
  1571. else
  1572. {
  1573. // The token is now removed, we can release it
  1574. m_aDeletedTokens[iIndex]->Release();
  1575. }
  1576. }
  1577. m_iDeletedTokens = 0;
  1578. // The added token list's tokens were added as they were put onto the list,
  1579. // so just clear the list so that they stay added at the end
  1580. m_iAddedTokens = 0;
  1581. // Now we don't care about the old user because of the apply
  1582. m_dstrOldUserTokenId.Clear();
  1583. m_pCurUserToken->GetId( &m_dstrOldUserTokenId );
  1584. ChangeDefaultUser();
  1585. // Kick the engine to pick up the changes.
  1586. // Note that the recoprofile change would have taken effect when
  1587. // we selected that list item, and that there is no way to
  1588. // pick up the audio changes right now since SetInput() is not
  1589. // implemented for shared engines.
  1590. if ( fRecognizerChange || fAudioChange )
  1591. {
  1592. BOOL fRecoContextInitialized = FALSE;
  1593. if (fRecognizerChange)
  1594. {
  1595. ulFlags |= SRDLGF_RECOGNIZER;
  1596. }
  1597. if (fAudioChange)
  1598. {
  1599. ulFlags |= SRDLGF_AUDIOINPUT;
  1600. }
  1601. HRESULT hr = CreateRecoContext( &fRecoContextInitialized, FALSE, ulFlags);
  1602. if ( FAILED( hr ) )
  1603. {
  1604. RecoContextError( fRecoContextInitialized, TRUE, hr );
  1605. }
  1606. if ( fRecognizerChange )
  1607. {
  1608. SPDBG_ASSERT( pToken );
  1609. m_pDefaultRecToken = pToken;
  1610. }
  1611. EngineSelChange();
  1612. }
  1613. if(m_cpRecoEngine)
  1614. {
  1615. m_cpRecoEngine->SetRecoState( SPRST_ACTIVE );
  1616. }
  1617. } /* CSRDlg::OnApply */
  1618. /************************************************************
  1619. * CSRDlg::OnDrawItem
  1620. *
  1621. * Description:
  1622. * Handles drawing items in the list view
  1623. *********************************************** BRENTMID ***/
  1624. void CSRDlg::OnDrawItem( HWND hWnd, const DRAWITEMSTRUCT * pDrawStruct )
  1625. {
  1626. RECT rcClip;
  1627. LVITEM lvi;
  1628. UINT uiFlags = ILD_TRANSPARENT;
  1629. HIMAGELIST himl;
  1630. int cxImage = 0, cyImage = 0;
  1631. UINT uFirstColWidth;
  1632. // Get the item image to be displayed
  1633. lvi.mask = LVIF_IMAGE | LVIF_STATE | LVIF_PARAM;
  1634. lvi.iItem = pDrawStruct->itemID;
  1635. lvi.iSubItem = 0;
  1636. ListView_GetItem(pDrawStruct->hwndItem, &lvi);
  1637. // We want to be drawing the current default as selected
  1638. LVFINDINFO lvfi;
  1639. lvfi.flags = LVFI_PARAM;
  1640. lvfi.lParam = (LPARAM) m_pCurUserToken;
  1641. UINT uiCurDefaultIndex = (UINT)::SendMessage( m_hUserList, LVM_FINDITEM, -1, (LPARAM) &lvfi );
  1642. bool fSelected = (uiCurDefaultIndex == pDrawStruct->itemID);
  1643. // Check to see if this item is selected
  1644. if ( fSelected )
  1645. {
  1646. // Set the text background and foreground colors
  1647. SetTextColor(pDrawStruct->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
  1648. SetBkColor(pDrawStruct->hDC, GetSysColor(COLOR_HIGHLIGHT));
  1649. }
  1650. else
  1651. {
  1652. // Set the text background and foreground colors to the standard window
  1653. // colors
  1654. SetTextColor(pDrawStruct->hDC, GetSysColor(COLOR_WINDOWTEXT));
  1655. SetBkColor(pDrawStruct->hDC, GetSysColor(COLOR_WINDOW));
  1656. }
  1657. // Get the image list and draw the image.
  1658. // The image list will consist of the checked box and the unchecked box
  1659. // for the LVS_EX_CHECKBOXES style
  1660. himl = ListView_GetImageList(pDrawStruct->hwndItem, LVSIL_STATE);
  1661. if (himl)
  1662. {
  1663. // For a LVS_EX_CHECKBOXES style, image 0 is unchecked and image 1 is checked
  1664. ImageList_Draw(himl,
  1665. fSelected ? 1 : 0,
  1666. pDrawStruct->hDC,
  1667. pDrawStruct->rcItem.left, pDrawStruct->rcItem.top,
  1668. uiFlags);
  1669. // Find out how big the image we just drew was
  1670. ImageList_GetIconSize(himl, &cxImage, &cyImage);
  1671. }
  1672. // Calculate the width of the first column after the image width. If
  1673. // There was no image, then cxImage will be zero.
  1674. LVCOLUMN pColumn;
  1675. pColumn.mask = LVCF_WIDTH;
  1676. ::SendMessage( m_hUserList, LVM_GETCOLUMN, 0, (LPARAM)&pColumn );
  1677. int iColWidth = pColumn.cx; // pixel width of "Description Column"
  1678. uFirstColWidth = iColWidth - cxImage;
  1679. // Set up the new clipping rect for the first column text and draw it
  1680. rcClip.left = pDrawStruct->rcItem.left + cxImage;
  1681. rcClip.right = pDrawStruct->rcItem.left + iColWidth;
  1682. rcClip.top = pDrawStruct->rcItem.top;
  1683. rcClip.bottom = pDrawStruct->rcItem.bottom;
  1684. ISpObjectToken *pToken = (ISpObjectToken *) lvi.lParam;
  1685. CSpDynamicString dstrTokenName;
  1686. SpGetDescription(pToken, &dstrTokenName);
  1687. DrawItemColumn(pDrawStruct->hDC, dstrTokenName, &rcClip);
  1688. // If we changed the colors for the selected item, undo it
  1689. if ( fSelected )
  1690. {
  1691. // Set the text background and foreground colors
  1692. SetTextColor(pDrawStruct->hDC, GetSysColor(COLOR_WINDOWTEXT));
  1693. SetBkColor(pDrawStruct->hDC, GetSysColor(COLOR_WINDOW));
  1694. }
  1695. // If the item is focused, now draw a focus rect around the entire row
  1696. if (pDrawStruct->itemState & ODS_FOCUS)
  1697. {
  1698. // Adjust the left edge to exclude the image
  1699. rcClip = pDrawStruct->rcItem;
  1700. rcClip.left += cxImage;
  1701. // Draw the focus rect
  1702. if ( ::GetFocus() == m_hUserList )
  1703. {
  1704. DrawFocusRect(pDrawStruct->hDC, &rcClip);
  1705. }
  1706. }
  1707. } // CSRDlg::OnDrawItem
  1708. /************************************************************
  1709. * CSRDlg::DrawItemColumn
  1710. *
  1711. * Description:
  1712. * Handles drawing of the column data
  1713. *********************************************** BRENTMID ***/
  1714. void CSRDlg::DrawItemColumn(HDC hdc, WCHAR* lpsz, LPRECT prcClip)
  1715. {
  1716. USES_CONVERSION;
  1717. int iHeight = 0; // Will cause CreateFont() to use default in case we
  1718. // don't get the height below
  1719. // Get the height of the text
  1720. if (hdc)
  1721. {
  1722. TEXTMETRIC tm;
  1723. if (GetTextMetrics(hdc, &tm))
  1724. {
  1725. iHeight = tm.tmHeight;
  1726. }
  1727. }
  1728. // link the font
  1729. LCID dwLCID = GetUserDefaultLCID();
  1730. // Pick an appropriate font. On Windows 2000, let the system fontlink.
  1731. DWORD dwVersion = GetVersion();
  1732. HFONT hfontNew = NULL;
  1733. HFONT hfontOld = NULL;
  1734. if ( (dwVersion >= 0x80000000)
  1735. || (LOBYTE(LOWORD(dwVersion)) < 5 ) )
  1736. {
  1737. // Less than NT5: Figure out what font
  1738. WCHAR achCodePage[6];
  1739. UINT uiCodePage;
  1740. if (0 != GetLocaleInfo(dwLCID, LOCALE_IDEFAULTANSICODEPAGE, achCodePage, 6))
  1741. {
  1742. uiCodePage = _wtoi(achCodePage);
  1743. }
  1744. else
  1745. {
  1746. uiCodePage = GetACP();
  1747. }
  1748. CComPtr<IMultiLanguage> cpMultiLanguage;
  1749. MIMECPINFO MimeCpInfo;
  1750. if ( SUCCEEDED(cpMultiLanguage.CoCreateInstance(CLSID_CMultiLanguage))
  1751. && SUCCEEDED(cpMultiLanguage->GetCodePageInfo(uiCodePage, &MimeCpInfo)))
  1752. {
  1753. USES_CONVERSION;
  1754. hfontNew = CreateFont(iHeight, 0, 0, 0, FW_NORMAL, 0, 0, 0,
  1755. MimeCpInfo.bGDICharset,
  1756. OUT_DEFAULT_PRECIS,
  1757. CLIP_DEFAULT_PRECIS,
  1758. DEFAULT_QUALITY,
  1759. DEFAULT_PITCH,
  1760. MimeCpInfo.wszProportionalFont);
  1761. }
  1762. cpMultiLanguage.Release();
  1763. }
  1764. if ( hfontNew )
  1765. {
  1766. hfontOld = (HFONT) ::SelectObject( hdc, hfontNew );
  1767. }
  1768. CSpDynamicString szString;
  1769. CSpDynamicString szNewString;
  1770. // Check to see if the string fits in the clip rect. If not, truncate
  1771. // the string and add "...".
  1772. szString = lpsz;
  1773. szNewString = CalcStringEllipsis(hdc, szString, UNLEN, prcClip->right - prcClip->left);
  1774. szString = szNewString;
  1775. // print the text
  1776. ExtTextOutW(hdc, prcClip->left + 2, prcClip->top + 2, ETO_CLIPPED | ETO_OPAQUE,
  1777. prcClip, szString.m_psz, szString.Length(), NULL);
  1778. // Replace the old font
  1779. if ( hfontNew )
  1780. {
  1781. ::SelectObject( hdc, hfontOld );
  1782. ::DeleteObject( hfontNew );
  1783. }
  1784. }
  1785. /************************************************************
  1786. * CSRDlg::CalcStringEllipsis
  1787. *
  1788. * Description:
  1789. * If the text won't fit in the box, edit it, and make
  1790. * it have ellipses
  1791. *********************************************** BRENTMID ***/
  1792. CSpDynamicString CSRDlg::CalcStringEllipsis(HDC hdc, CSpDynamicString lpszString, int cchMax, UINT uColWidth)
  1793. {
  1794. USES_CONVERSION;
  1795. WCHAR szEllipsis[] = L"...";
  1796. SIZE sizeString;
  1797. SIZE sizeEllipsis;
  1798. int cbString;
  1799. CSpDynamicString lpszTemp;
  1800. BOOL fSuccess = FALSE;
  1801. // Adjust the column width to take into account the edges
  1802. uColWidth -= 4;
  1803. lpszTemp = lpszString;
  1804. // Get the width of the string in pixels
  1805. cbString = lpszTemp.Length();
  1806. if (!::GetTextExtentPoint32(hdc, lpszTemp, cbString, &sizeString))
  1807. {
  1808. SPDBG_ASSERT(FALSE);
  1809. }
  1810. // If the width of the string is greater than the column width shave
  1811. // the string and add the ellipsis
  1812. if ((ULONG)sizeString.cx > uColWidth)
  1813. {
  1814. if (!::GetTextExtentPoint32(hdc, szEllipsis, lstrlen(szEllipsis),
  1815. &sizeEllipsis))
  1816. {
  1817. SPDBG_ASSERT(FALSE);
  1818. }
  1819. while ((cbString > 0) && (fSuccess == FALSE))
  1820. {
  1821. lpszTemp[--cbString] = 0;
  1822. if (!::GetTextExtentPoint32(hdc, lpszTemp, cbString, &sizeString))
  1823. {
  1824. SPDBG_ASSERT(FALSE);
  1825. }
  1826. if ((ULONG)(sizeString.cx + sizeEllipsis.cx) <= uColWidth)
  1827. {
  1828. // The string with the ellipsis finally fits, now make sure
  1829. // there is enough room in the string for the ellipsis
  1830. if (cchMax >= (cbString + lstrlen(szEllipsis)))
  1831. {
  1832. // Concatenate the two strings and break out of the loop
  1833. lpszTemp.Append( szEllipsis );
  1834. lpszString = lpszTemp;
  1835. fSuccess = TRUE;
  1836. }
  1837. }
  1838. }
  1839. }
  1840. else
  1841. {
  1842. // No need to do anything, everything fits great.
  1843. fSuccess = TRUE;
  1844. }
  1845. return (lpszString);
  1846. } // CSRDlg::CalStringEllipsis
  1847. /************************************************************
  1848. * CSRDlg::ChangeDefaultUser
  1849. *
  1850. * Description:
  1851. * Handles changes to the environment settings
  1852. *********************************************** BRENTMID ***/
  1853. void CSRDlg::ChangeDefaultUser()
  1854. {
  1855. HRESULT hr;
  1856. if (m_pCurUserToken)
  1857. {
  1858. hr = SpSetDefaultTokenForCategoryId(SPCAT_RECOPROFILES, m_pCurUserToken);
  1859. }
  1860. // Sort the items initially
  1861. ::SendMessage( m_hUserList, LVM_SORTITEMS, (LPARAM)m_pCurUserToken, LPARAM(&SortCols) );
  1862. } // CSRDlg::ChangeDefaultUser
  1863. /************************************************************
  1864. * CSRDlg::OnCancel
  1865. *
  1866. * Description:
  1867. * Handles undoing changes to the environment settings
  1868. *********************************************** BRENTMID ***/
  1869. void CSRDlg::OnCancel()
  1870. {
  1871. // Get the original user and make sure that is still the default.
  1872. // Note that in general m_pCurUserToken does not AddRef the
  1873. // ISpObjectToken it points to, so this is OK.
  1874. SpGetTokenFromId( m_dstrOldUserTokenId, &m_pCurUserToken );
  1875. ChangeDefaultUser();
  1876. // Set the old recoprofile so that none of the profiles added in this
  1877. // session will be in use:
  1878. // This allows us to roll back the adds below
  1879. // m_pCurUserToken does the trick since it is guaranteed to have
  1880. // been around before this session
  1881. if (m_cpRecoEngine)
  1882. {
  1883. m_cpRecoEngine->SetRecoState( SPRST_INACTIVE );
  1884. m_cpRecoEngine->SetRecoProfile( m_pCurUserToken );
  1885. m_cpRecoEngine->SetRecoState( SPRST_ACTIVE );
  1886. }
  1887. // Roll back and delete any new profiles added
  1888. int cItems = (int) ::SendMessage( m_hUserList, LVM_GETITEMCOUNT, 0, 0 );
  1889. LVITEM lvitem;
  1890. for ( int i = 0; i < m_iAddedTokens; i++ )
  1891. {
  1892. // Look for the list item with a ref out on this token.
  1893. // We need to do this because in order for a token to be successfully
  1894. // removed the only existing ref to that token has to call the Remove()
  1895. // method. The list is holding a ref to that item.
  1896. bool fFound = false;
  1897. for ( int j=0; !fFound && (j < cItems); j++ )
  1898. {
  1899. ::memset( &lvitem, 0, sizeof( lvitem ) );
  1900. lvitem.iItem = j;
  1901. lvitem.mask = LVIF_PARAM;
  1902. ::SendMessage( m_hUserList, LVM_GETITEM, 0, (LPARAM) &lvitem );
  1903. CSpDynamicString dstrItemId;
  1904. ISpObjectToken *pItemToken = (ISpObjectToken *) lvitem.lParam;
  1905. if ( pItemToken )
  1906. {
  1907. HRESULT hrId = pItemToken->GetId( &dstrItemId );
  1908. if ( SUCCEEDED( hrId ) &&
  1909. dstrItemId && m_aAddedTokens[i] &&
  1910. ( 0 == wcscmp( dstrItemId, m_aAddedTokens[ i ] ) ) )
  1911. {
  1912. // Should this fail, the profile just doesn't get removed: big deal
  1913. pItemToken->Remove( NULL );
  1914. fFound = true;
  1915. }
  1916. }
  1917. }
  1918. }
  1919. // We AddRefed it...
  1920. m_pCurUserToken->Release();
  1921. } // CSRDlg::OnCancel
  1922. /*****************************************************************************
  1923. * CSRDlg::EngineSelChange *
  1924. *-------------------------*
  1925. * Description:
  1926. * This function updates the list box when the user selects a new engine.
  1927. * If queries the token to see which UI items the engine supports.
  1928. * The parameter fInitialize determines if the engine is actually created.
  1929. * It does NOT actually change the default engine.
  1930. ****************************************************************** MIKEAR ***/
  1931. void CSRDlg::EngineSelChange(BOOL fInitialize)
  1932. {
  1933. HRESULT hr = S_OK;
  1934. SPDBG_FUNC( "CSRDlg::EngineSelChange" );
  1935. int iSelected = (int) ::SendMessage( m_hSRCombo, CB_GETCURSEL, 0, 0 );
  1936. ISpObjectToken *pToken = (ISpObjectToken *) ::SendMessage( m_hSRCombo, CB_GETITEMDATA, iSelected, 0 );
  1937. if ( CB_ERR == (LRESULT) pToken )
  1938. {
  1939. pToken = NULL;
  1940. }
  1941. if (pToken)
  1942. {
  1943. // Now the current reco token is the one we got off the currently-selected combobox item
  1944. m_pCurRecoToken = pToken;
  1945. // Kick the UI to enable the Apply button if necessary
  1946. KickCPLUI();
  1947. HRESULT hrRecoContextOK = S_OK;
  1948. if(fInitialize)
  1949. {
  1950. BOOL fContextInitialized = FALSE;
  1951. hrRecoContextOK = CreateRecoContext(&fContextInitialized, TRUE);
  1952. if ( FAILED( hrRecoContextOK ) )
  1953. {
  1954. RecoContextError( fContextInitialized, true, hrRecoContextOK );
  1955. }
  1956. }
  1957. if ( FAILED( hrRecoContextOK ) )
  1958. {
  1959. // Don't continue, all the buttons are grayed out,
  1960. // which is what we want
  1961. return;
  1962. }
  1963. }
  1964. // Check for something being wrong, in which case we want to gray out all
  1965. // the UI and stop here.
  1966. // For instance, if we had trouble creating the reco context that's supposed to
  1967. // be on now (the one for m_pDefaultRecToken), we certainly shouldn't
  1968. // enable the UI buttons...
  1969. if ( !pToken || (!m_cpRecoCtxt && (pToken == m_pDefaultRecToken)) )
  1970. {
  1971. RecoContextError( FALSE, FALSE );
  1972. return;
  1973. }
  1974. // Determine if the training UI component is supported.
  1975. // We can pass the current reco engine in as an argument only
  1976. // if it's the same as the one who's token we're asking about.
  1977. IUnknown *punkObject = (pToken == m_pDefaultRecToken) ? m_cpRecoEngine : NULL;
  1978. BOOL fSupported = FALSE;
  1979. hr = pToken->IsUISupported(SPDUI_UserTraining, NULL, 0, punkObject, &fSupported);
  1980. if (FAILED(hr))
  1981. {
  1982. fSupported = FALSE;
  1983. }
  1984. ::EnableWindow(::GetDlgItem(m_hDlg, IDC_USERTRAINING), fSupported);
  1985. // Determine if the Mic Wiz UI component is supported
  1986. fSupported = FALSE;
  1987. hr = pToken->IsUISupported(SPDUI_MicTraining, NULL, 0, punkObject, &fSupported);
  1988. if (FAILED(hr))
  1989. {
  1990. fSupported = FALSE;
  1991. }
  1992. ::EnableWindow(::GetDlgItem(m_hDlg, IDC_MICWIZ), fSupported);
  1993. // Determine if the Engine Prop UI component is supported
  1994. fSupported = FALSE;
  1995. hr = pToken->IsUISupported(SPDUI_EngineProperties, NULL, 0, punkObject, &fSupported);
  1996. if (FAILED(hr))
  1997. {
  1998. fSupported = FALSE;
  1999. }
  2000. ::EnableWindow(::GetDlgItem(m_hDlg, IDC_SR_ADV), fSupported);
  2001. // Determine if the Reco Profile Prop UI component is supported
  2002. fSupported = FALSE;
  2003. hr = pToken->IsUISupported(SPDUI_RecoProfileProperties, NULL, 0, punkObject, &fSupported);
  2004. if (FAILED(hr))
  2005. {
  2006. fSupported = FALSE;
  2007. }
  2008. ::EnableWindow(::GetDlgItem(m_hDlg, IDC_MODIFY), fSupported);
  2009. } /* CSRDlg::EngineSelChange */
  2010. /*****************************************************************************
  2011. * CSRDlg::IsCurRecoEngineAndCurRecoTokenMatch *
  2012. *---------------------------------------------*
  2013. * Description:
  2014. * Returns true in pfMatch iff the m_pCurRecoToken is the same
  2015. * as the token for m_cpRecoEngine.
  2016. * Return:
  2017. * S_OK
  2018. * E_POINTER
  2019. * Failed HRESULTs from any of the SAPI calls
  2020. ****************************************************************** BECKYW ***/
  2021. HRESULT CSRDlg::IsCurRecoEngineAndCurRecoTokenMatch( bool *pfMatch )
  2022. {
  2023. if ( !pfMatch )
  2024. {
  2025. return E_POINTER;
  2026. }
  2027. if ( !m_cpRecoEngine || !m_pCurRecoToken )
  2028. {
  2029. return E_FAIL;
  2030. }
  2031. *pfMatch = false;
  2032. // This gets the object token for the engine
  2033. CComPtr<ISpObjectToken> cpRecoEngineToken;
  2034. HRESULT hr = m_cpRecoEngine->GetRecognizer( &cpRecoEngineToken );
  2035. WCHAR *pwszRecoEngineTokenID = NULL;
  2036. WCHAR *pwszCurRecoTokenID = NULL;
  2037. if ( SUCCEEDED( hr ) )
  2038. {
  2039. hr = cpRecoEngineToken->GetId( &pwszRecoEngineTokenID );
  2040. }
  2041. if ( SUCCEEDED( hr ) )
  2042. {
  2043. hr = m_pCurRecoToken->GetId( &pwszCurRecoTokenID );
  2044. }
  2045. if ( pwszRecoEngineTokenID && pwszCurRecoTokenID )
  2046. {
  2047. *pfMatch = ( 0 == wcscmp( pwszRecoEngineTokenID, pwszCurRecoTokenID ) );
  2048. }
  2049. return hr;
  2050. } /* CSRDlg::IsCurRecoEngineAndCurRecoTokenMatch */