Leaked source code of windows server 2003
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.

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