#include "private.h" #include "sapilayr.h" #include "globals.h" #include "lbarsink.h" #include "immxutil.h" #include "mui.h" #include "slbarid.h" #include "nui.h" ////////////////////////////////////////////////////////////////////////////// // // CLangBarSink // ////////////////////////////////////////////////////////////////////////////// //+--------------------------------------------------------------------------- // // IUnknown // //---------------------------------------------------------------------------- STDAPI CLangBarSink::QueryInterface(REFIID riid, void **ppvObj) { *ppvObj = NULL; if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfLangBarEventSink)) { *ppvObj = SAFECAST(this, ITfLangBarEventSink *); } if (*ppvObj) { AddRef(); return S_OK; } return E_NOINTERFACE; } STDAPI_(ULONG) CLangBarSink::AddRef() { return ++m_cRef; } STDAPI_(ULONG) CLangBarSink::Release() { m_cRef--; Assert(m_cRef >= 0); if (m_cRef == 0) { delete this; return 0; } return m_cRef; } //+--------------------------------------------------------------------------- // // ctor // //---------------------------------------------------------------------------- CLangBarSink::CLangBarSink(CSpTask *pSpTask) { Dbg_MemSetThisName(TEXT("CLangBarSink")); Assert(pSpTask); m_pSpTask = pSpTask; m_pSpTask->AddRef(); m_nNumItem = 0; m_fInitSink = FALSE; m_fPosted = FALSE; m_fGrammarBuiltOut = FALSE; m_hDynRule = NULL; m_cRef = 1; } //+--------------------------------------------------------------------------- // // dtor // //---------------------------------------------------------------------------- CLangBarSink::~CLangBarSink() { if (m_cplbm) { m_cplbm->UnadviseEventSink(m_dwlbimCookie); } _UninitItemList(); SafeRelease(m_pSpTask); } //+--------------------------------------------------------------------------- // // SetFocus // //---------------------------------------------------------------------------- HRESULT CLangBarSink::OnSetFocus(DWORD dwThreadId) { TraceMsg(TF_LB_SINK, "CLangBarSink::OnSetFocus, dwThreadId=%d",dwThreadId); if (m_fPosted == TRUE) return S_OK; HWND hwnd = m_pSpTask->GetTip()->_GetWorkerWnd(); if (hwnd) { PostMessage(hwnd, WM_PRIV_LBARSETFOCUS, 0, 0); m_fPosted = TRUE; } return S_OK; } HRESULT CLangBarSink::_OnSetFocus() { HRESULT hr = S_OK; CSapiIMX *pime = m_pSpTask->GetTip(); TraceMsg(TF_LB_SINK, "LBSINK: _OnSetFocus is called back"); if ( !pime ) return E_FAIL; // this _tim check is needed because on Win98 the worker window's // winproc may get called after the window is destroyed. // In theory we should be ok since we destory the window which calls // _OnSetFocus() via private message before we release tim // if (pime->_tim && pime->IsActiveThread() == S_OK) { // do we have to do anything? hr = _InitItemList(); BOOL fCmdOn; fCmdOn = pime->GetOnOff( ) && pime->GetDICTATIONSTAT_CommandingOnOff( ); // the dynamic toolbar grammar is available only for Voice command mode. if ( fCmdOn && pime->_LanguageBarCmdEnabled( )) { if (hr==S_OK && !m_fGrammarBuiltOut && pime->_IsDictationActiveForLang(GetPlatformResourceLangID())) { // build C/C grammar hr = _BuildGrammar(); _ActivateGrammar(TRUE); } } } m_fPosted = FALSE; return hr; } //+--------------------------------------------------------------------------- // // ThreadTerminate // //---------------------------------------------------------------------------- HRESULT CLangBarSink::OnThreadTerminate(DWORD dwThreadId) { // // check if the thread is us, release the dynamic grammar object // via sptask // _UninitItemList(); return S_OK; } //+--------------------------------------------------------------------------- // // OnThreadItemChange // //---------------------------------------------------------------------------- HRESULT CLangBarSink::OnThreadItemChange(DWORD dwThreadId) { //PerfConsider: This is called many times when assembly changes // This will be corrected in the future but for now // we re-initialize unnecessary things again/again. // check if the thread is us, // to un-initialize the grammar then rebuild the one TraceMsg(TF_LB_SINK, "CLangBarSink::OnThreadItemChange, dwThreadId=%d", dwThreadId); _UninitItemList(); OnSetFocus(dwThreadId); // call sptask to rebuild grammar here return S_OK; } //+--------------------------------------------------------------------------- // // Init // //---------------------------------------------------------------------------- HRESULT CLangBarSink::Init() { TraceMsg(TF_LB_SINK, "CLangBarSink::Init is called"); HRESULT hr = _EnsureLangBarMgrs(); if (!m_fInitSink) { // the sink leaks if we call this twice if (S_OK == hr) { hr = m_cplbm->AdviseEventSink(this, NULL, 0, &m_dwlbimCookie); } m_fInitSink = TRUE; } return hr; } //+--------------------------------------------------------------------------- // // Uninit // //---------------------------------------------------------------------------- HRESULT CLangBarSink::Uninit() { TraceMsg(TF_LB_SINK, "CLangBarSink::Uninit is called"); if (m_cplbm) { m_cplbm->UnadviseEventSink(m_dwlbimCookie); m_cplbm.Release(); } return S_OK; } //+--------------------------------------------------------------------------- // // _EnsureLangBarMgrs // //---------------------------------------------------------------------------- HRESULT CLangBarSink::_EnsureLangBarMgrs() { HRESULT hr = S_OK; TraceMsg(TF_LB_SINK, "CLangBarSink::_EnsureLangBarMgrs is called"); if (!m_cplbm) { hr = TF_CreateLangBarMgr(&m_cplbm); } if (S_OK == hr && !m_cplbim) { DWORD dw; hr = m_cplbm->GetThreadLangBarItemMgr(GetCurrentThreadId(), &m_cplbim, &dw); } return hr; } //+--------------------------------------------------------------------------- // // _AddLBarItem // //---------------------------------------------------------------------------- void CLangBarSink::_AddLBarItem(ITfLangBarItem *plbItem) { if (plbItem) { int nCnt = m_rgItem.Count(); if (m_rgItem.Insert(nCnt, 1)) { plbItem->AddRef(); m_rgItem.Set(nCnt, plbItem); m_nNumItem++; } } } //+--------------------------------------------------------------------------- // // _InitItemList // //---------------------------------------------------------------------------- HRESULT CLangBarSink::_InitItemList() { TraceMsg(TF_LB_SINK, "CLangBarSink::_InitItemList is called"); if (0 != m_nNumItem) { TraceMsg(TF_LB_SINK, "m_nNumItem=%d, Don't continue InitItemList",m_nNumItem); return S_OK; } HRESULT hr = E_FAIL; CComPtr cpEnum; Assert(m_cplbim); if (SUCCEEDED(hr = m_cplbim->EnumItems(&cpEnum))) { ITfLangBarItem * plbi; while (S_OK == cpEnum->Next(1, &plbi, NULL)) { hr = S_OK; // OK if there's at least one DWORD dwStatus; plbi->GetStatus(&dwStatus); // add buttons that are not diabled or hidden if ((dwStatus & (TF_LBI_STATUS_HIDDEN|TF_LBI_STATUS_DISABLED))==0) { _AddLBarItem(plbi); } plbi->Release(); } } return hr; } //+--------------------------------------------------------------------------- // // _UninitItemList // //---------------------------------------------------------------------------- void CLangBarSink::_UninitItemList() { TraceMsg(TF_LB_SINK, "CLangBarSink::_UninitItemList is called"); if (int nCnt = m_rgItem.Count()) { int i = 0; while (i < nCnt) { ITfLangBarItem * plbi = m_rgItem.Get(i); if (plbi) plbi->Release(); i++; } m_rgItem.Clear(); } m_nNumItem = 0; _UnloadGrammar(); } //+--------------------------------------------------------------------------- // // _BuildGrammar // // synopsis: build a C&C grammar based on text labels of langbar // items // // BuildGrammar( ) works only when the mode is in Voice command mode. // // we have make sure only when voice command is ON and dictation command // is enabled, this function is called. //---------------------------------------------------------------------------- HRESULT CLangBarSink::_BuildGrammar() { HRESULT hr = E_FAIL; // get sptask and create a grammar TraceMsg(TF_LB_SINK, "_BuildGrammar is called"); if (m_pSpTask) { CComPtr cpReco; // get the grammar loaded to the dictation reco context // // it will use the Voice command mode recon context. hr = m_pSpTask->GetRecoContextForCommand(&cpReco); TraceMsg(TF_LB_SINK, "TBarGrammar: GetRecoContextForCommand, hr=%x", hr); if (S_OK == hr) { // we don't need to re-create grammar object if (!m_cpSpGrammar) { hr = cpReco->CreateGrammar(GRAM_ID_TBCMD, &m_cpSpGrammar); TraceMsg(TF_LB_SINK, "TBarGrammar: Create TOOLBar Grammar"); } } if (S_OK == hr) { hr = m_cpSpGrammar->ResetGrammar(GetPlatformResourceLangID()); TraceMsg(TF_LB_SINK, "TBarGrammar: ResetGrammar"); } if (S_OK == hr) { // get the rule handle m_cpSpGrammar->GetRule(GetToolbarCommandRuleName(), RULE_ID_TBCMD, SPRAF_TopLevel|SPRAF_Active|SPRAF_Dynamic, TRUE, &m_hDynRule); TraceMsg(TF_LB_SINK, "TBarGrammar:Get Rule Handle"); // then activate the rule } if (S_OK == hr) { // enumerate all the buttons, // see if they are either ITfLangBarItemBitmapButton // or ITfLangBarItemButton, that have OnClick method on them BSTR bstr; int nBtns = m_rgItem.Count(); for (int i = 0; i < nBtns; i++) { GUID guidItem; if (_GetButtonText(i, &bstr, &guidItem) && bstr) { // item and property // the item can include optional string (?please etc) // if (_IsItemEnabledForCommand(guidItem)) if ( !IsEqualGUID(guidItem, GUID_LBI_SAPILAYR_COMMANDING) ) { SPPROPERTYINFO pi = {0}; pi.pszName = bstr; m_cpSpGrammar->AddWordTransition(m_hDynRule, NULL, bstr, L" ", SPWT_LEXICAL, (float)1.01, &pi); TraceMsg(TF_LB_SINK, "TBarGrammar: button %S added to grammar", bstr); } SysFreeString(bstr); } } // // add a bogus string that has significant weight so we out weight // others // SPPROPERTYINFO pi = {0}; const WCHAR c_szBogus[] = L"zhoulotskunosprok"; pi.pszName = c_szBogus; m_cpSpGrammar->AddWordTransition(m_hDynRule, NULL, c_szBogus, L" ", SPWT_LEXICAL, (float)1000.01, &pi); TraceMsg(TF_LB_SINK, "TBarGrammar: start commit ..."); m_cpSpGrammar->Commit(0); TraceMsg(TF_LB_SINK, "TBarGrammar:Done commit ..."); m_fGrammarBuiltOut = TRUE; } } TraceMsg(TF_LB_SINK, "_BuildGrammar is done!!!!"); return hr; } //+--------------------------------------------------------------------------- // // _UnloadGrammar // // //---------------------------------------------------------------------------- HRESULT CLangBarSink::_UnloadGrammar() { // clear the rule HRESULT hr = S_OK; TraceMsg(TF_LB_SINK, "CLangBarSink::_UnloadGrammar is called"); if (m_cpSpGrammar) { hr = _ActivateGrammar(FALSE); if (S_OK == hr) { hr = m_cpSpGrammar->ClearRule(m_hDynRule); if ( hr == S_OK ) m_fGrammarBuiltOut = FALSE; // Next time, the grammar needs to be rebuilt. } } return hr; } //+--------------------------------------------------------------------------- // // _ActivateGrammar // // synopsis: // //---------------------------------------------------------------------------- HRESULT CLangBarSink::_ActivateGrammar(BOOL fActive) { HRESULT hr = S_OK; TraceMsg(TF_LB_SINK, "TBarGrammar: ActivateGrammar=%d", fActive); if (m_cpSpGrammar) { m_cpSpGrammar->SetRuleState(GetToolbarCommandRuleName(), NULL, fActive ? SPRS_ACTIVE : SPRS_INACTIVE); } TraceMsg(TF_LB_SINK, "TBarGrammar: ActivateGrammar is done"); return hr; } //+--------------------------------------------------------------------------- // // ProcessToolBarCommand // // When return value is TRUE, there is corresponding button on the toolbar // otherwise the return value is FALSE //---------------------------------------------------------------------------- BOOL CLangBarSink::ProcessToolbarCmd(const WCHAR *szProperty) { BOOL fRet=FALSE; Assert(szProperty); // go through items in the array and call onclick method // if there is a match if (szProperty) { int nBtns = m_rgItem.Count(); for (int i = 0; i < nBtns; i++) { BSTR bstr; if (_GetButtonText(i, &bstr, NULL) && bstr) { if (0 == wcscmp(szProperty, bstr)) { HRESULT hr = E_FAIL; CComPtr cplbiBtn ; CComPtr cplbiBmpBtn ; POINT pt = {0, 0}; ITfLangBarItem * plbi = m_rgItem.Get(i); if (plbi) { hr = plbi->QueryInterface(IID_ITfLangBarItemButton, (void **)&cplbiBtn); #ifndef TOOLBAR_CMD_FOR_MENUS // this code removes the toolbar command from thoese // items with menus TF_LANGBARITEMINFO info; if (S_OK == hr) { hr = plbi->GetInfo(&info); } if (info.dwStyle & TF_LBI_STYLE_BTN_MENU) { // do not click on buttons with menu items // since we don't hanle commands for the items } else #endif if (S_OK == hr) { // is it OK to call OnClick without specifying rect? hr = cplbiBtn->OnClick(TF_LBI_CLK_LEFT, pt, NULL); // OnClick would start a new edit session for some buttons, such // as "Correction" // // The return value could be TS_S_ASYNC or S_OK depends on how // the application grants the edit request. // // We need to check if the hr value is successful. // not only S_OK. if ( SUCCEEDED(hr) ) fRet = TRUE; } #ifdef TOOLBAR_CMD_FOR_MENUS TF_LANGBARITEMINFO info; RECT rc = {0}; if (S_OK == hr) { hr = plbi->GetInfo(&info); } if (S_OK == hr) { hr = m_cplbim->GetItemFloatingRect(0, info.guidItem, &rc); } if (S_OK == hr) { HWND hwnd = FindWindow(NULL, TF_FLOATINGLANGBAR_WNDTITLEA); if (hwnd) { DWORD dw; POINT poi; poi.x = (rc.right + rc.left)/2, poi.y = (rc.top + rc.bottom)/2, ::ScreenToClient(hwnd, &poi); dw = MAKELONG(LOWORD(poi.x), LOWORD(poi.y)); PostMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, dw); PostMessage(hwnd, WM_LBUTTONUP, 0, dw); } } #endif } if (!cplbiBtn) { hr = plbi->QueryInterface(IID_ITfLangBarItemBitmapButton, (void **)&cplbiBmpBtn); if (S_OK == hr) { hr = cplbiBtn->OnClick(TF_LBI_CLK_LEFT, pt, NULL); if ( S_OK == hr ) fRet = TRUE; } } break; } // if (0 == wcscmpi(szProperty, bstr)) SysFreeString(bstr); } // if (_GetButtonText(i, bstr)) } // for } return fRet; } //+--------------------------------------------------------------------------- // // GetButtonText // //---------------------------------------------------------------------------- BOOL CLangBarSink::_GetButtonText(int iBtn, BSTR *pbstr, GUID *pguid) { HRESULT hr = E_FAIL; CComPtr cplbiBtn ; CComPtr cplbiBmpBtn ; Assert(iBtn < m_rgItem.Count()); Assert(pbstr); *pbstr = NULL; ITfLangBarItem * plbi = m_rgItem.Get(iBtn); if (plbi) { hr = plbi->QueryInterface(IID_ITfLangBarItemButton, (void **)&cplbiBtn); if (S_OK == hr) { hr = cplbiBtn->GetTooltipString(pbstr); } } // only in case when the button does not have a // regular interface we'd qi for bitmapbutton if (!cplbiBtn) { hr = plbi->QueryInterface(IID_ITfLangBarItemBitmapButton, (void **)&cplbiBmpBtn); if (S_OK == hr) { hr = cplbiBmpBtn->GetTooltipString(pbstr); } } TF_LANGBARITEMINFO Info = {0}; if (S_OK == hr) { hr = plbi->GetInfo(&Info); } if (S_OK == hr) { if (pguid) { memcpy(pguid, &(Info.guidItem), sizeof(GUID)); } if (Info.dwStyle & TF_LBI_STYLE_BTN_MENU) { // do not create commands for buttons with menu items // since we don't hanle commands for the items hr = S_FALSE; } } if (S_OK != hr && *pbstr) { // avoid mem leak SysFreeString(*pbstr); } return S_OK == hr; } //+--------------------------------------------------------------------------- // // OnModalInput // //---------------------------------------------------------------------------- STDAPI CLangBarSink::OnModalInput(DWORD dwThreadId, UINT uMsg, WPARAM wParam, LPARAM lParam) { return E_NOTIMPL; } //+--------------------------------------------------------------------------- // // ShowFloating // //---------------------------------------------------------------------------- STDAPI CLangBarSink::ShowFloating(DWORD dwFlags) { return E_NOTIMPL; } //+--------------------------------------------------------------------------- // // GetItemFloatingRect // //---------------------------------------------------------------------------- STDAPI CLangBarSink::GetItemFloatingRect(DWORD dwThreadId, REFGUID rguid, RECT *prc) { return E_NOTIMPL; }