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.
 
 
 
 
 
 

538 lines
17 KiB

//
// property.cpp
//
// Property code.
//
#include "globals.h"
#include "mark.h"
#include "editsess.h"
#include "pstore.h"
// callback code for CPropertyEditSession
#define VIEW_CASE_PROPERTY 0
#define SET_CASE_PROPERTY 1
#define VIEW_CUSTOM_PROPERTY 2
#define SET_CUSTOM_PROPERTY 3
const TCHAR c_szWorkerWndClass[] = TEXT("Mark Worker Wnd Class");
class CPropertyEditSession : public CEditSessionBase
{
public:
CPropertyEditSession(CMarkTextService *pMark, ITfContext *pContext, ULONG ulCallback) : CEditSessionBase(pContext)
{
_pMark = pMark;
_pMark->AddRef();
_ulCallback = ulCallback;
}
~CPropertyEditSession()
{
_pMark->Release();
}
// ITfEditSession
STDMETHODIMP DoEditSession(TfEditCookie ec)
{
switch (_ulCallback)
{
case VIEW_CASE_PROPERTY:
_pMark->_ViewCaseProperty(ec, _pContext);
break;
case SET_CASE_PROPERTY:
_pMark->_SetCaseProperty(ec, _pContext);
break;
case VIEW_CUSTOM_PROPERTY:
_pMark->_ViewCustomProperty(ec, _pContext);
break;
case SET_CUSTOM_PROPERTY:
_pMark->_SetCustomProperty(ec, _pContext);
break;
}
return S_OK;
}
private:
CMarkTextService *_pMark;
ULONG _ulCallback;
};
//+---------------------------------------------------------------------------
//
// _RequestEditSession
//
// Helper function. Schedules an edit session for a particular property
// related callback.
//----------------------------------------------------------------------------
void CMarkTextService::_RequestPropertyEditSession(ULONG ulCallback)
{
ITfDocumentMgr *pFocusDoc;
ITfContext *pContext;
CPropertyEditSession *pPropertyEditSession;
HRESULT hr;
// get the focus document
if (_pThreadMgr->GetFocus(&pFocusDoc) != S_OK)
return;
if (pFocusDoc == NULL)
return; // no focus
// we want the topmost context, since the main doc context could be
// superceded by a modal tip context
if (pFocusDoc->GetTop(&pContext) != S_OK)
{
pContext = NULL;
goto Exit;
}
if (pPropertyEditSession = new CPropertyEditSession(this, pContext, ulCallback))
{
// we need a document write lock
// the CPropertyEditSession will do all the work when the
// CPropertyEditSession::DoEditSession method is called by the context
pContext->RequestEditSession(_tfClientId, pPropertyEditSession, TF_ES_READWRITE | TF_ES_ASYNCDONTCARE, &hr);
pPropertyEditSession->Release();
}
Exit:
SafeRelease(pContext);
pFocusDoc->Release();
}
//+---------------------------------------------------------------------------
//
// _SetCaseProperty
//
//----------------------------------------------------------------------------
void CMarkTextService::_SetCaseProperty(TfEditCookie ec, ITfContext *pContext)
{
TF_SELECTION tfSelection;
ITfProperty *pCaseProperty;
ITfRange *pRangeChar;
WCHAR ch;
ULONG cchRead;
ULONG cFetched;
VARIANT varValue;
// get the case property
if (pContext->GetProperty(c_guidCaseProperty, &pCaseProperty) != S_OK)
return;
// get the selection
if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) != S_OK ||
cFetched != 1)
{
// no selection or something went wrong
tfSelection.range = NULL;
goto Exit;
}
// get a helper range ready for the loop
if (tfSelection.range->Clone(&pRangeChar) != S_OK)
goto Exit;
// set the value char-by-char over the selection
while (TRUE)
{
// read one char, the TF_TF_MOVESTART flag will advance the start anchor
if (tfSelection.range->GetText(ec, TF_TF_MOVESTART, &ch, 1, &cchRead) != S_OK)
break;
// any more text to read?
if (cchRead != 1)
break;
// make pRange cover just the one char we read
if (pRangeChar->ShiftEndToRange(ec, tfSelection.range, TF_ANCHOR_START) != S_OK)
break;
// set the value
varValue.vt = VT_I4;
varValue.lVal = (ch >= 'A' && ch <= 'Z');
if (pCaseProperty->SetValue(ec, pRangeChar, &varValue) != S_OK)
break;
// advance pRange for next iteration
if (pRangeChar->Collapse(ec, TF_ANCHOR_END) != S_OK)
break;
}
pRangeChar->Release();
Exit:
SafeRelease(tfSelection.range);
pCaseProperty->Release();
}
//+---------------------------------------------------------------------------
//
// _Menu_OnSetCaseProperty
//
// Callback for the "Set Case Property" menu item.
// Set the value for a private "case" property over the text covered by the
// selection. The case property is private to this text service, which defines
// it as:
//
// static compact, per character
// VT_I4, !0 => character is within 'A' - 'Z', 0 => anything else.
//
//----------------------------------------------------------------------------
/* static */
void CMarkTextService::_Menu_OnSetCaseProperty(CMarkTextService *_this)
{
_this->_RequestPropertyEditSession(SET_CASE_PROPERTY);
}
//+---------------------------------------------------------------------------
//
// _ViewCaseProperty
//
//----------------------------------------------------------------------------
void CMarkTextService::_ViewCaseProperty(TfEditCookie ec, ITfContext *pContext)
{
TF_SELECTION tfSelection;
ITfProperty *pCaseProperty;
ULONG cchRead;
LONG cch;
ULONG cFetched;
ULONG i;
VARIANT varValue;
// get the case property
if (pContext->GetProperty(c_guidCaseProperty, &pCaseProperty) != S_OK)
return;
// get the selection
if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) != S_OK ||
cFetched != 1)
{
// no selection or something went wrong
tfSelection.range = NULL;
goto Exit;
}
// grab the text
if (tfSelection.range->GetText(ec, 0, _achDisplayText, ARRAYSIZE(_achDisplayText)-1, &cchRead) != S_OK)
goto Exit;
// prepare for the loop
if (tfSelection.range->Collapse(ec, TF_ANCHOR_START) != S_OK)
goto Exit;
// get the property value char-by-char over the selection
for (i=0; i < cchRead; i++)
{
// advance pRange for next iteration, cover the next char
if (tfSelection.range->ShiftStartToRange(ec, tfSelection.range, TF_ANCHOR_END) != S_OK)
break;
if (tfSelection.range->ShiftEnd(ec, 1, &cch, NULL) != S_OK)
break;
if (cch != 1) // hit a region boundary?
break;
switch (pCaseProperty->GetValue(ec, tfSelection.range, &varValue))
{
case S_OK:
// the property value has been set, use it
// 'U' --> uppercase
// 'L' --> lowercase
_achDisplayPropertyText[i] = varValue.lVal ? 'U' : 'L';
break;
case S_FALSE:
// no property value set, varValue.vt == VT_EMPTY
// '?' --> no value
_achDisplayPropertyText[i] = '?';
break;
default:
// error
// '!' --> error
_achDisplayPropertyText[i] = '!';
break;
}
}
for (; i<cchRead; i++) // error case
{
_achDisplayPropertyText[i] = '!';
}
_achDisplayPropertyText[cchRead] = '\0';
_achDisplayText[cchRead] = '\0';
// we can't change the focus while holding a lock
// so postpone the UI until we've released our lock
PostMessage(_hWorkerWnd, CMarkTextService::WM_DISPLAY_PROPERTY, 0, 0);
Exit:
SafeRelease(tfSelection.range);
pCaseProperty->Release();
}
//+---------------------------------------------------------------------------
//
// _Menu_OnViewCaseProperty
//
// Menu callback. Displays a popup with "case" property values over the
// current selection.
//----------------------------------------------------------------------------
/* static */
void CMarkTextService::_Menu_OnViewCaseProperty(CMarkTextService *_this)
{
_this->_RequestPropertyEditSession(VIEW_CASE_PROPERTY);
}
//+---------------------------------------------------------------------------
//
// _ViewCustomProperty
//
// Display the value of this text service's custom property over the text
// covered by the selection.
//----------------------------------------------------------------------------
void CMarkTextService::_ViewCustomProperty(TfEditCookie ec, ITfContext *pContext)
{
TF_SELECTION tfSelection;
ITfProperty *pCustomProperty;
ITfRange *pSelRange;
ITfRange *pPropertySpanRange;
ULONG cchRead;
ULONG cFetched;
LONG cch;
VARIANT varValue;
HRESULT hr;
// get the case property
if (pContext->GetProperty(c_guidCustomProperty, &pCustomProperty) != S_OK)
return;
// get the selection
if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) != S_OK ||
cFetched != 1)
{
// no selection or something went wrong
pSelRange = NULL;
goto Exit;
}
// free up tfSelection so we can re-use it below
pSelRange = tfSelection.range;
// the selection may not exactly match a span of text covered by the
// custom property....so we'll return the value over the start anchor of
// the selection.
// we need to collapse the range because GetValue will return VT_EMPTY
// if the query range is not completely covered by the property span
if (pSelRange->Collapse(ec, TF_ANCHOR_START) != S_OK)
goto Exit;
// the query range must also cover at least one char
if (pSelRange->ShiftEnd(ec, 1, &cch, NULL) != S_OK)
goto Exit;
hr = pCustomProperty->GetValue(ec, pSelRange, &varValue);
switch (hr)
{
case S_OK:
// there's a value at the selection start anchor
// let's find out exactly what text is covered
_achDisplayText[0] = '\0';
if (pCustomProperty->FindRange(ec, pSelRange, &pPropertySpanRange, TF_ANCHOR_START) == S_OK)
{
if (pPropertySpanRange->GetText(ec, 0, _achDisplayText, ARRAYSIZE(_achDisplayText)-1, &cchRead) != S_OK)
{
cchRead = 0;
}
_achDisplayText[cchRead] = '\0';
// let's update the selection to give the user feedback
tfSelection.range = pPropertySpanRange;
pContext->SetSelection(ec, 1, &tfSelection);
pPropertySpanRange->Release();
}
// write the value
wsprintfW(_achDisplayPropertyText, L"%i", varValue.lVal);
break;
case S_FALSE:
// the property has no value, varValue.vt == VT_EMPTY
_achDisplayText[0] = '\0';
SafeStringCopy(_achDisplayPropertyText, ARRAYSIZE(_achDisplayPropertyText), L"- No Value -");
break;
default:
goto Exit; // error
}
// we can't change the focus while holding a lock
// so postpone the UI until we've released our lock
PostMessage(_hWorkerWnd, CMarkTextService::WM_DISPLAY_PROPERTY, 0, 0);
Exit:
SafeRelease(pSelRange);
pCustomProperty->Release();
}
//+---------------------------------------------------------------------------
//
// _Menu_OnViewCustomProperty
//
// Menu callback for "View Custom Property".
//----------------------------------------------------------------------------
/* static */
void CMarkTextService::_Menu_OnViewCustomProperty(CMarkTextService *_this)
{
_this->_RequestPropertyEditSession(VIEW_CUSTOM_PROPERTY);
}
//+---------------------------------------------------------------------------
//
// _InitWorkerWnd
//
// Called from Activate. Create a worker window to receive private windows
// messages.
//----------------------------------------------------------------------------
BOOL CMarkTextService::_InitWorkerWnd()
{
WNDCLASS wc;
memset(&wc, 0, sizeof(wc));
wc.lpfnWndProc = _WorkerWndProc;
wc.hInstance = g_hInst;
wc.lpszClassName = c_szWorkerWndClass;
if (RegisterClass(&wc) == 0)
return FALSE;
_hWorkerWnd = CreateWindow(c_szWorkerWndClass, TEXT("Mark Worker Wnd"),
0, 0, 0, 0, 0, NULL, NULL, g_hInst, this);
return (_hWorkerWnd != NULL);
}
//+---------------------------------------------------------------------------
//
// _UninitWorkerWnd
//
// Called from Deactivate. Destroy the worker window.
//----------------------------------------------------------------------------
void CMarkTextService::_UninitWorkerWnd()
{
if (_hWorkerWnd != NULL)
{
DestroyWindow(_hWorkerWnd);
_hWorkerWnd = NULL;
}
UnregisterClass(c_szWorkerWndClass, g_hInst);
}
//+---------------------------------------------------------------------------
//
// _WorkerWndProc
//
//----------------------------------------------------------------------------
/* static */
LRESULT CALLBACK CMarkTextService::_WorkerWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CMarkTextService *_this;
int cch;
char achText[128];
switch (uMsg)
{
case WM_CREATE:
// save the this pointer we originally passed into CreateWindow
SetWindowLongPtr(hWnd, GWLP_USERDATA,
(LONG_PTR)((CREATESTRUCT *)lParam)->lpCreateParams);
return 0;
case WM_DISPLAY_PROPERTY:
_this = (CMarkTextService *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
// bring up a message box with the contents of _achDisplayText
// first, convert from unicode
cch = WideCharToMultiByte(CP_ACP, 0, _this->_achDisplayText, wcslen(_this->_achDisplayText),
achText, ARRAYSIZE(achText)-1, NULL, NULL);
if (cch < ARRAYSIZE(achText) - 1)
{
achText[cch++] = '\n';
}
if (cch < ARRAYSIZE(achText) - 1)
{
cch += WideCharToMultiByte(CP_ACP, 0, _this->_achDisplayPropertyText, wcslen(_this->_achDisplayPropertyText),
achText+cch, ARRAYSIZE(achText)-cch-1, NULL, NULL);
}
achText[cch] = '\0';
// bring up the display
MessageBoxA(NULL, achText, "Property View", MB_OK);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
//+---------------------------------------------------------------------------
//
// _Menu_OnSetCustomProperty
//
// Callback for the "Set Custom Property" menu item.
//----------------------------------------------------------------------------
/* static */
void CMarkTextService::_Menu_OnSetCustomProperty(CMarkTextService *_this)
{
_this->_RequestPropertyEditSession(SET_CUSTOM_PROPERTY);
}
//+---------------------------------------------------------------------------
//
// _SetCustomProperty
//
// Assign a custom property to the text covered by the selection.
//----------------------------------------------------------------------------
void CMarkTextService::_SetCustomProperty(TfEditCookie ec, ITfContext *pContext)
{
TF_SELECTION tfSelection;
ITfProperty *pCustomProperty;
CCustomPropertyStore *pCustomPropertyStore;
ULONG cFetched;
// get the case property
if (pContext->GetProperty(c_guidCustomProperty, &pCustomProperty) != S_OK)
return;
// get the selection
if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) != S_OK ||
cFetched != 1)
{
// no selection or something went wrong
tfSelection.range = NULL;
goto Exit;
}
if ((pCustomPropertyStore = new CCustomPropertyStore) == NULL)
goto Exit;
pCustomProperty->SetValueStore(ec, tfSelection.range, pCustomPropertyStore);
// TSF will hold a reference to pCustomPropertyStore is the SetValueStore succeeded
// but we need to release ours
pCustomPropertyStore->Release();
Exit:
pCustomProperty->Release();
SafeRelease(tfSelection.range);
}