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.
463 lines
12 KiB
463 lines
12 KiB
//
|
|
// snoop.cpp
|
|
//
|
|
// CSnoopWnd implementation.
|
|
//
|
|
|
|
#include "globals.h"
|
|
#include "snoop.h"
|
|
#include "case.h"
|
|
#include "editsess.h"
|
|
|
|
class CUpdateTextEditSession : public CEditSessionBase
|
|
{
|
|
public:
|
|
CUpdateTextEditSession(ITfContext *pContext, ITfRange *pRange, CSnoopWnd *pSnoopWnd) : CEditSessionBase(pContext)
|
|
{
|
|
_pSnoopWnd = pSnoopWnd;
|
|
_pRange = pRange;
|
|
if (_pRange != NULL)
|
|
{
|
|
_pRange->AddRef();
|
|
}
|
|
}
|
|
~CUpdateTextEditSession()
|
|
{
|
|
SafeRelease(_pRange);
|
|
}
|
|
|
|
// ITfEditSession
|
|
STDMETHODIMP DoEditSession(TfEditCookie ec);
|
|
|
|
private:
|
|
CSnoopWnd *_pSnoopWnd;
|
|
ITfRange *_pRange;
|
|
};
|
|
|
|
|
|
#define SNOOP_X_POS 0
|
|
#define SNOOP_Y_POS 0
|
|
|
|
#define SNOOP_WIDTH 300
|
|
#define SNOOP_HEIGHT (SNOOP_WIDTH / 3)
|
|
|
|
ATOM CSnoopWnd::_atomWndClass = 0;
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// ctor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CSnoopWnd::CSnoopWnd(CCaseTextService *pCase)
|
|
{
|
|
_pCase = pCase; // no AddRef because CSnoopWnd is contained in the
|
|
// pCase lifetime
|
|
_hWnd = NULL;
|
|
_cchText = 0;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _InitClass
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
/* static */
|
|
BOOL CSnoopWnd::_InitClass()
|
|
{
|
|
WNDCLASS wc;
|
|
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = CSnoopWnd::_WndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = g_hInst;
|
|
wc.hIcon = NULL;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = NULL;
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = TEXT("SnoopWndClass");
|
|
|
|
_atomWndClass = RegisterClass(&wc);
|
|
|
|
return (_atomWndClass != 0);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _UninitClass
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
/* static */
|
|
void CSnoopWnd::_UninitClass()
|
|
{
|
|
if (_atomWndClass != 0)
|
|
{
|
|
UnregisterClass((LPCTSTR)_atomWndClass, g_hInst);
|
|
}
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _Init
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CSnoopWnd::_Init()
|
|
{
|
|
// nb: on windows 2000, you can use WS_EX_NOACTIVATE to prevent windows
|
|
// from taking the foreground. We don't use that here for compatibility.
|
|
// Instead, we use WS_DISABLED, which can be burdensome for more complex
|
|
// ui.
|
|
|
|
_hWnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TOOLWINDOW,
|
|
(LPCTSTR)_atomWndClass,
|
|
TEXT("Snoop Window"),
|
|
WS_BORDER | WS_DISABLED | WS_POPUP,
|
|
SNOOP_X_POS, SNOOP_Y_POS,
|
|
SNOOP_WIDTH, SNOOP_HEIGHT,
|
|
NULL,
|
|
NULL,
|
|
g_hInst,
|
|
this);
|
|
|
|
return (_hWnd != NULL);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _Uninit
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CSnoopWnd::_Uninit()
|
|
{
|
|
if (_hWnd != NULL)
|
|
{
|
|
DestroyWindow(_hWnd);
|
|
_hWnd = NULL;
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _Show
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CSnoopWnd::_Show()
|
|
{
|
|
ShowWindow(_hWnd, SW_SHOWNA);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _Hide
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CSnoopWnd::_Hide()
|
|
{
|
|
ShowWindow(_hWnd, SW_HIDE);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _UpdateText
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CSnoopWnd::_UpdateText(ITfRange *pRange)
|
|
{
|
|
ITfDocumentMgr *pdmFocus;
|
|
ITfContext *pContext;
|
|
CUpdateTextEditSession *pEditSession;
|
|
HRESULT hr;
|
|
|
|
if (pRange == NULL)
|
|
{
|
|
// caller wants us to just use the selection in the focus doc
|
|
if (_pCase->_GetThreadMgr()->GetFocus(&pdmFocus) != S_OK)
|
|
return;
|
|
|
|
hr = pdmFocus->GetTop(&pContext);
|
|
|
|
pdmFocus->Release();
|
|
|
|
if (hr != S_OK)
|
|
return;
|
|
}
|
|
else if (pRange->GetContext(&pContext) != S_OK)
|
|
return;
|
|
|
|
if (pEditSession = new CUpdateTextEditSession(pContext, pRange, this))
|
|
{
|
|
// we need a document read lock to scan text
|
|
// the CUpdateTextEditSession will do all the work when the
|
|
// CUpdateTextEditSession::DoEditSession method is called by the context
|
|
pContext->RequestEditSession(_pCase->_GetClientId(), pEditSession, TF_ES_READ | TF_ES_ASYNCDONTCARE, &hr);
|
|
|
|
pEditSession->Release();
|
|
}
|
|
|
|
pContext->Release();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// DoEditSession
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CUpdateTextEditSession::DoEditSession(TfEditCookie ec)
|
|
{
|
|
_pSnoopWnd->_UpdateText(ec, _pContext, _pRange);
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _UpdateText
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CSnoopWnd::_UpdateText(TfEditCookie ec, ITfContext *pContext, ITfRange *pRange)
|
|
{
|
|
LONG cchBefore;
|
|
LONG cchAfter;
|
|
TF_SELECTION tfSelection;
|
|
ULONG cFetched;
|
|
BOOL fReleaseRange = FALSE;
|
|
|
|
if (pRange == NULL)
|
|
{
|
|
// caller wants us to use the selection
|
|
if (pContext->GetSelection(ec, TS_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) != S_OK ||
|
|
cFetched != 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
pRange = tfSelection.range; // no AddRef, take ownership of the pointer
|
|
fReleaseRange = TRUE;
|
|
}
|
|
|
|
// arbitrarily grab some text before and after the range start anchor
|
|
|
|
pRange->Collapse(ec, TF_ANCHOR_START);
|
|
|
|
pRange->ShiftStart(ec, -MAX_SNOOP_TEXT / 2, &cchBefore, NULL);
|
|
|
|
cchBefore = -cchBefore; // we shifted backwards, so make count a positive number
|
|
|
|
pRange->GetText(ec, 0, _achText, cchBefore, (ULONG *)&cchBefore);
|
|
|
|
pRange->Collapse(ec, TF_ANCHOR_END);
|
|
|
|
pRange->ShiftEnd(ec, MAX_SNOOP_TEXT - cchBefore, &cchAfter, NULL);
|
|
|
|
pRange->GetText(ec, 0, _achText + cchBefore, cchAfter, (ULONG *)&cchAfter);
|
|
|
|
_cchText = cchBefore + cchAfter;
|
|
|
|
// force a repaint
|
|
|
|
InvalidateRect(_hWnd, NULL, TRUE);
|
|
|
|
if (fReleaseRange)
|
|
{
|
|
pRange->Release();
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _WndProc
|
|
//
|
|
// Snoop window proc.
|
|
//----------------------------------------------------------------------------
|
|
|
|
/* static */
|
|
LRESULT CALLBACK CSnoopWnd::_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HDC hdc;
|
|
PAINTSTRUCT ps;
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_CREATE:
|
|
_SetThis(hWnd, lParam);
|
|
return 0;
|
|
|
|
case WM_PAINT:
|
|
hdc = BeginPaint(hWnd, &ps);
|
|
_GetThis(hWnd)->_OnPaint(hWnd, hdc);
|
|
EndPaint(hWnd, &ps);
|
|
return 0;
|
|
}
|
|
|
|
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _OnPaint
|
|
//
|
|
// WM_PAINT handler for CSnoopWnd.
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CSnoopWnd::_OnPaint(HWND hWnd, HDC hdc)
|
|
{
|
|
RECT rc;
|
|
|
|
// background
|
|
GetClientRect(hWnd, &rc);
|
|
FillRect(hdc, &rc, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
|
|
|
|
// text
|
|
TextOutW(hdc, 0, 0, _achText, _cchText);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _InitSnoopWnd
|
|
//
|
|
// Create and init the snoop window.
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CCaseTextService::_InitSnoopWnd()
|
|
{
|
|
BOOL fThreadfocus;
|
|
ITfSource *pSource = NULL;
|
|
|
|
// create a snoop window
|
|
|
|
if ((_pSnoopWnd = new CSnoopWnd(this)) == NULL)
|
|
return FALSE;
|
|
|
|
if (!_pSnoopWnd->_Init())
|
|
goto ExitError;
|
|
|
|
// we also need a thread focus sink
|
|
|
|
if (_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) != S_OK)
|
|
{
|
|
pSource = NULL;
|
|
goto ExitError;
|
|
}
|
|
|
|
if (pSource->AdviseSink(IID_ITfThreadFocusSink, (ITfThreadFocusSink *)this, &_dwThreadFocusSinkCookie) != S_OK)
|
|
{
|
|
// make sure we don't try to Unadvise _dwThreadFocusSinkCookie later
|
|
_dwThreadFocusSinkCookie = TF_INVALID_COOKIE;
|
|
goto ExitError;
|
|
}
|
|
|
|
pSource->Release();
|
|
|
|
// we may need to display the snoop window right now
|
|
// our thread focus sink won't be called until something changes,
|
|
// so we need to check the current state.
|
|
|
|
if (_pThreadMgr->IsThreadFocus(&fThreadfocus) == S_OK && fThreadfocus)
|
|
{
|
|
OnSetThreadFocus();
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
ExitError:
|
|
SafeRelease(pSource);
|
|
_UninitSnoopWnd();
|
|
return FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _UninitSnoopWnd
|
|
//
|
|
// Uninit and free the snoop window, unadvise the thread focus sink.
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CCaseTextService::_UninitSnoopWnd()
|
|
{
|
|
ITfSource *pSource;
|
|
|
|
if (_pSnoopWnd != NULL)
|
|
{
|
|
_pSnoopWnd->_Uninit();
|
|
delete _pSnoopWnd;
|
|
}
|
|
|
|
if (_dwThreadFocusSinkCookie != TF_INVALID_COOKIE)
|
|
{
|
|
if (_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) == S_OK)
|
|
{
|
|
pSource->UnadviseSink(_dwThreadFocusSinkCookie);
|
|
pSource->Release();
|
|
}
|
|
|
|
_dwThreadFocusSinkCookie = TF_INVALID_COOKIE;
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _Menu_ShowSnoopWnd
|
|
//
|
|
// Show or hide the snoop window.
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CCaseTextService::_Menu_ShowSnoopWnd(CCaseTextService *_this)
|
|
{
|
|
_this->_fShowSnoop = !_this->_fShowSnoop;
|
|
|
|
if (_this->_fShowSnoop)
|
|
{
|
|
_this->_pSnoopWnd->_Show();
|
|
}
|
|
else
|
|
{
|
|
_this->_pSnoopWnd->_Hide();
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// OnSetThreadFocus
|
|
//
|
|
// Called by the system when the thread/appartment of this text service gains
|
|
// the ui focus.
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CCaseTextService::OnSetThreadFocus()
|
|
{
|
|
if (_fShowSnoop)
|
|
{
|
|
_pSnoopWnd->_Show();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// OnKillThreadFocus
|
|
//
|
|
// Called by the system when the thread/appartment of this text service loses
|
|
// the ui focus.
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDAPI CCaseTextService::OnKillThreadFocus()
|
|
{
|
|
// only show our snoop window when our thread has the focus.
|
|
if (_fShowSnoop)
|
|
{
|
|
_pSnoopWnd->_Hide();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|