WindowsXP-SP1/windows/advcore/ctf/sapilayr/lbarsink.cpp
2020-09-30 16:53:49 +02:00

747 lines
21 KiB
C++

#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<IEnumTfLangBarItems> 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<ISpRecoContext> 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<ITfLangBarItemButton> cplbiBtn ;
CComPtr<ITfLangBarItemBitmapButton> 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<ITfLangBarItemButton> cplbiBtn ;
CComPtr<ITfLangBarItemBitmapButton> 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;
}