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

  1. //
  2. // property.cpp
  3. //
  4. // Property code.
  5. //
  6. #include "globals.h"
  7. #include "mark.h"
  8. #include "editsess.h"
  9. #include "pstore.h"
  10. // callback code for CPropertyEditSession
  11. #define VIEW_CASE_PROPERTY 0
  12. #define SET_CASE_PROPERTY 1
  13. #define VIEW_CUSTOM_PROPERTY 2
  14. #define SET_CUSTOM_PROPERTY 3
  15. const TCHAR c_szWorkerWndClass[] = TEXT("Mark Worker Wnd Class");
  16. class CPropertyEditSession : public CEditSessionBase
  17. {
  18. public:
  19. CPropertyEditSession(CMarkTextService *pMark, ITfContext *pContext, ULONG ulCallback) : CEditSessionBase(pContext)
  20. {
  21. _pMark = pMark;
  22. _pMark->AddRef();
  23. _ulCallback = ulCallback;
  24. }
  25. ~CPropertyEditSession()
  26. {
  27. _pMark->Release();
  28. }
  29. // ITfEditSession
  30. STDMETHODIMP DoEditSession(TfEditCookie ec)
  31. {
  32. switch (_ulCallback)
  33. {
  34. case VIEW_CASE_PROPERTY:
  35. _pMark->_ViewCaseProperty(ec, _pContext);
  36. break;
  37. case SET_CASE_PROPERTY:
  38. _pMark->_SetCaseProperty(ec, _pContext);
  39. break;
  40. case VIEW_CUSTOM_PROPERTY:
  41. _pMark->_ViewCustomProperty(ec, _pContext);
  42. break;
  43. case SET_CUSTOM_PROPERTY:
  44. _pMark->_SetCustomProperty(ec, _pContext);
  45. break;
  46. }
  47. return S_OK;
  48. }
  49. private:
  50. CMarkTextService *_pMark;
  51. ULONG _ulCallback;
  52. };
  53. //+---------------------------------------------------------------------------
  54. //
  55. // _RequestEditSession
  56. //
  57. // Helper function. Schedules an edit session for a particular property
  58. // related callback.
  59. //----------------------------------------------------------------------------
  60. void CMarkTextService::_RequestPropertyEditSession(ULONG ulCallback)
  61. {
  62. ITfDocumentMgr *pFocusDoc;
  63. ITfContext *pContext;
  64. CPropertyEditSession *pPropertyEditSession;
  65. HRESULT hr;
  66. // get the focus document
  67. if (_pThreadMgr->GetFocus(&pFocusDoc) != S_OK)
  68. return;
  69. if (pFocusDoc == NULL)
  70. return; // no focus
  71. // we want the topmost context, since the main doc context could be
  72. // superceded by a modal tip context
  73. if (pFocusDoc->GetTop(&pContext) != S_OK)
  74. {
  75. pContext = NULL;
  76. goto Exit;
  77. }
  78. if (pPropertyEditSession = new CPropertyEditSession(this, pContext, ulCallback))
  79. {
  80. // we need a document write lock
  81. // the CPropertyEditSession will do all the work when the
  82. // CPropertyEditSession::DoEditSession method is called by the context
  83. pContext->RequestEditSession(_tfClientId, pPropertyEditSession, TF_ES_READWRITE | TF_ES_ASYNCDONTCARE, &hr);
  84. pPropertyEditSession->Release();
  85. }
  86. Exit:
  87. SafeRelease(pContext);
  88. pFocusDoc->Release();
  89. }
  90. //+---------------------------------------------------------------------------
  91. //
  92. // _SetCaseProperty
  93. //
  94. //----------------------------------------------------------------------------
  95. void CMarkTextService::_SetCaseProperty(TfEditCookie ec, ITfContext *pContext)
  96. {
  97. TF_SELECTION tfSelection;
  98. ITfProperty *pCaseProperty;
  99. ITfRange *pRangeChar;
  100. WCHAR ch;
  101. ULONG cchRead;
  102. ULONG cFetched;
  103. VARIANT varValue;
  104. // get the case property
  105. if (pContext->GetProperty(c_guidCaseProperty, &pCaseProperty) != S_OK)
  106. return;
  107. // get the selection
  108. if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) != S_OK ||
  109. cFetched != 1)
  110. {
  111. // no selection or something went wrong
  112. tfSelection.range = NULL;
  113. goto Exit;
  114. }
  115. // get a helper range ready for the loop
  116. if (tfSelection.range->Clone(&pRangeChar) != S_OK)
  117. goto Exit;
  118. // set the value char-by-char over the selection
  119. while (TRUE)
  120. {
  121. // read one char, the TF_TF_MOVESTART flag will advance the start anchor
  122. if (tfSelection.range->GetText(ec, TF_TF_MOVESTART, &ch, 1, &cchRead) != S_OK)
  123. break;
  124. // any more text to read?
  125. if (cchRead != 1)
  126. break;
  127. // make pRange cover just the one char we read
  128. if (pRangeChar->ShiftEndToRange(ec, tfSelection.range, TF_ANCHOR_START) != S_OK)
  129. break;
  130. // set the value
  131. varValue.vt = VT_I4;
  132. varValue.lVal = (ch >= 'A' && ch <= 'Z');
  133. if (pCaseProperty->SetValue(ec, pRangeChar, &varValue) != S_OK)
  134. break;
  135. // advance pRange for next iteration
  136. if (pRangeChar->Collapse(ec, TF_ANCHOR_END) != S_OK)
  137. break;
  138. }
  139. pRangeChar->Release();
  140. Exit:
  141. SafeRelease(tfSelection.range);
  142. pCaseProperty->Release();
  143. }
  144. //+---------------------------------------------------------------------------
  145. //
  146. // _Menu_OnSetCaseProperty
  147. //
  148. // Callback for the "Set Case Property" menu item.
  149. // Set the value for a private "case" property over the text covered by the
  150. // selection. The case property is private to this text service, which defines
  151. // it as:
  152. //
  153. // static compact, per character
  154. // VT_I4, !0 => character is within 'A' - 'Z', 0 => anything else.
  155. //
  156. //----------------------------------------------------------------------------
  157. /* static */
  158. void CMarkTextService::_Menu_OnSetCaseProperty(CMarkTextService *_this)
  159. {
  160. _this->_RequestPropertyEditSession(SET_CASE_PROPERTY);
  161. }
  162. //+---------------------------------------------------------------------------
  163. //
  164. // _ViewCaseProperty
  165. //
  166. //----------------------------------------------------------------------------
  167. void CMarkTextService::_ViewCaseProperty(TfEditCookie ec, ITfContext *pContext)
  168. {
  169. TF_SELECTION tfSelection;
  170. ITfProperty *pCaseProperty;
  171. ULONG cchRead;
  172. LONG cch;
  173. ULONG cFetched;
  174. ULONG i;
  175. VARIANT varValue;
  176. // get the case property
  177. if (pContext->GetProperty(c_guidCaseProperty, &pCaseProperty) != S_OK)
  178. return;
  179. // get the selection
  180. if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) != S_OK ||
  181. cFetched != 1)
  182. {
  183. // no selection or something went wrong
  184. tfSelection.range = NULL;
  185. goto Exit;
  186. }
  187. // grab the text
  188. if (tfSelection.range->GetText(ec, 0, _achDisplayText, ARRAYSIZE(_achDisplayText)-1, &cchRead) != S_OK)
  189. goto Exit;
  190. // prepare for the loop
  191. if (tfSelection.range->Collapse(ec, TF_ANCHOR_START) != S_OK)
  192. goto Exit;
  193. // get the property value char-by-char over the selection
  194. for (i=0; i < cchRead; i++)
  195. {
  196. // advance pRange for next iteration, cover the next char
  197. if (tfSelection.range->ShiftStartToRange(ec, tfSelection.range, TF_ANCHOR_END) != S_OK)
  198. break;
  199. if (tfSelection.range->ShiftEnd(ec, 1, &cch, NULL) != S_OK)
  200. break;
  201. if (cch != 1) // hit a region boundary?
  202. break;
  203. switch (pCaseProperty->GetValue(ec, tfSelection.range, &varValue))
  204. {
  205. case S_OK:
  206. // the property value has been set, use it
  207. // 'U' --> uppercase
  208. // 'L' --> lowercase
  209. _achDisplayPropertyText[i] = varValue.lVal ? 'U' : 'L';
  210. break;
  211. case S_FALSE:
  212. // no property value set, varValue.vt == VT_EMPTY
  213. // '?' --> no value
  214. _achDisplayPropertyText[i] = '?';
  215. break;
  216. default:
  217. // error
  218. // '!' --> error
  219. _achDisplayPropertyText[i] = '!';
  220. break;
  221. }
  222. }
  223. for (; i<cchRead; i++) // error case
  224. {
  225. _achDisplayPropertyText[i] = '!';
  226. }
  227. _achDisplayPropertyText[cchRead] = '\0';
  228. _achDisplayText[cchRead] = '\0';
  229. // we can't change the focus while holding a lock
  230. // so postpone the UI until we've released our lock
  231. PostMessage(_hWorkerWnd, CMarkTextService::WM_DISPLAY_PROPERTY, 0, 0);
  232. Exit:
  233. SafeRelease(tfSelection.range);
  234. pCaseProperty->Release();
  235. }
  236. //+---------------------------------------------------------------------------
  237. //
  238. // _Menu_OnViewCaseProperty
  239. //
  240. // Menu callback. Displays a popup with "case" property values over the
  241. // current selection.
  242. //----------------------------------------------------------------------------
  243. /* static */
  244. void CMarkTextService::_Menu_OnViewCaseProperty(CMarkTextService *_this)
  245. {
  246. _this->_RequestPropertyEditSession(VIEW_CASE_PROPERTY);
  247. }
  248. //+---------------------------------------------------------------------------
  249. //
  250. // _ViewCustomProperty
  251. //
  252. // Display the value of this text service's custom property over the text
  253. // covered by the selection.
  254. //----------------------------------------------------------------------------
  255. void CMarkTextService::_ViewCustomProperty(TfEditCookie ec, ITfContext *pContext)
  256. {
  257. TF_SELECTION tfSelection;
  258. ITfProperty *pCustomProperty;
  259. ITfRange *pSelRange;
  260. ITfRange *pPropertySpanRange;
  261. ULONG cchRead;
  262. ULONG cFetched;
  263. LONG cch;
  264. VARIANT varValue;
  265. HRESULT hr;
  266. // get the case property
  267. if (pContext->GetProperty(c_guidCustomProperty, &pCustomProperty) != S_OK)
  268. return;
  269. // get the selection
  270. if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) != S_OK ||
  271. cFetched != 1)
  272. {
  273. // no selection or something went wrong
  274. pSelRange = NULL;
  275. goto Exit;
  276. }
  277. // free up tfSelection so we can re-use it below
  278. pSelRange = tfSelection.range;
  279. // the selection may not exactly match a span of text covered by the
  280. // custom property....so we'll return the value over the start anchor of
  281. // the selection.
  282. // we need to collapse the range because GetValue will return VT_EMPTY
  283. // if the query range is not completely covered by the property span
  284. if (pSelRange->Collapse(ec, TF_ANCHOR_START) != S_OK)
  285. goto Exit;
  286. // the query range must also cover at least one char
  287. if (pSelRange->ShiftEnd(ec, 1, &cch, NULL) != S_OK)
  288. goto Exit;
  289. hr = pCustomProperty->GetValue(ec, pSelRange, &varValue);
  290. switch (hr)
  291. {
  292. case S_OK:
  293. // there's a value at the selection start anchor
  294. // let's find out exactly what text is covered
  295. _achDisplayText[0] = '\0';
  296. if (pCustomProperty->FindRange(ec, pSelRange, &pPropertySpanRange, TF_ANCHOR_START) == S_OK)
  297. {
  298. if (pPropertySpanRange->GetText(ec, 0, _achDisplayText, ARRAYSIZE(_achDisplayText)-1, &cchRead) != S_OK)
  299. {
  300. cchRead = 0;
  301. }
  302. _achDisplayText[cchRead] = '\0';
  303. // let's update the selection to give the user feedback
  304. tfSelection.range = pPropertySpanRange;
  305. pContext->SetSelection(ec, 1, &tfSelection);
  306. pPropertySpanRange->Release();
  307. }
  308. // write the value
  309. wsprintfW(_achDisplayPropertyText, L"%i", varValue.lVal);
  310. break;
  311. case S_FALSE:
  312. // the property has no value, varValue.vt == VT_EMPTY
  313. _achDisplayText[0] = '\0';
  314. SafeStringCopy(_achDisplayPropertyText, ARRAYSIZE(_achDisplayPropertyText), L"- No Value -");
  315. break;
  316. default:
  317. goto Exit; // error
  318. }
  319. // we can't change the focus while holding a lock
  320. // so postpone the UI until we've released our lock
  321. PostMessage(_hWorkerWnd, CMarkTextService::WM_DISPLAY_PROPERTY, 0, 0);
  322. Exit:
  323. SafeRelease(pSelRange);
  324. pCustomProperty->Release();
  325. }
  326. //+---------------------------------------------------------------------------
  327. //
  328. // _Menu_OnViewCustomProperty
  329. //
  330. // Menu callback for "View Custom Property".
  331. //----------------------------------------------------------------------------
  332. /* static */
  333. void CMarkTextService::_Menu_OnViewCustomProperty(CMarkTextService *_this)
  334. {
  335. _this->_RequestPropertyEditSession(VIEW_CUSTOM_PROPERTY);
  336. }
  337. //+---------------------------------------------------------------------------
  338. //
  339. // _InitWorkerWnd
  340. //
  341. // Called from Activate. Create a worker window to receive private windows
  342. // messages.
  343. //----------------------------------------------------------------------------
  344. BOOL CMarkTextService::_InitWorkerWnd()
  345. {
  346. WNDCLASS wc;
  347. memset(&wc, 0, sizeof(wc));
  348. wc.lpfnWndProc = _WorkerWndProc;
  349. wc.hInstance = g_hInst;
  350. wc.lpszClassName = c_szWorkerWndClass;
  351. if (RegisterClass(&wc) == 0)
  352. return FALSE;
  353. _hWorkerWnd = CreateWindow(c_szWorkerWndClass, TEXT("Mark Worker Wnd"),
  354. 0, 0, 0, 0, 0, NULL, NULL, g_hInst, this);
  355. return (_hWorkerWnd != NULL);
  356. }
  357. //+---------------------------------------------------------------------------
  358. //
  359. // _UninitWorkerWnd
  360. //
  361. // Called from Deactivate. Destroy the worker window.
  362. //----------------------------------------------------------------------------
  363. void CMarkTextService::_UninitWorkerWnd()
  364. {
  365. if (_hWorkerWnd != NULL)
  366. {
  367. DestroyWindow(_hWorkerWnd);
  368. _hWorkerWnd = NULL;
  369. }
  370. UnregisterClass(c_szWorkerWndClass, g_hInst);
  371. }
  372. //+---------------------------------------------------------------------------
  373. //
  374. // _WorkerWndProc
  375. //
  376. //----------------------------------------------------------------------------
  377. /* static */
  378. LRESULT CALLBACK CMarkTextService::_WorkerWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  379. {
  380. CMarkTextService *_this;
  381. int cch;
  382. char achText[128];
  383. switch (uMsg)
  384. {
  385. case WM_CREATE:
  386. // save the this pointer we originally passed into CreateWindow
  387. SetWindowLongPtr(hWnd, GWLP_USERDATA,
  388. (LONG_PTR)((CREATESTRUCT *)lParam)->lpCreateParams);
  389. return 0;
  390. case WM_DISPLAY_PROPERTY:
  391. _this = (CMarkTextService *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
  392. // bring up a message box with the contents of _achDisplayText
  393. // first, convert from unicode
  394. cch = WideCharToMultiByte(CP_ACP, 0, _this->_achDisplayText, wcslen(_this->_achDisplayText),
  395. achText, ARRAYSIZE(achText)-1, NULL, NULL);
  396. if (cch < ARRAYSIZE(achText) - 1)
  397. {
  398. achText[cch++] = '\n';
  399. }
  400. if (cch < ARRAYSIZE(achText) - 1)
  401. {
  402. cch += WideCharToMultiByte(CP_ACP, 0, _this->_achDisplayPropertyText, wcslen(_this->_achDisplayPropertyText),
  403. achText+cch, ARRAYSIZE(achText)-cch-1, NULL, NULL);
  404. }
  405. achText[cch] = '\0';
  406. // bring up the display
  407. MessageBoxA(NULL, achText, "Property View", MB_OK);
  408. return 0;
  409. }
  410. return DefWindowProc(hWnd, uMsg, wParam, lParam);
  411. }
  412. //+---------------------------------------------------------------------------
  413. //
  414. // _Menu_OnSetCustomProperty
  415. //
  416. // Callback for the "Set Custom Property" menu item.
  417. //----------------------------------------------------------------------------
  418. /* static */
  419. void CMarkTextService::_Menu_OnSetCustomProperty(CMarkTextService *_this)
  420. {
  421. _this->_RequestPropertyEditSession(SET_CUSTOM_PROPERTY);
  422. }
  423. //+---------------------------------------------------------------------------
  424. //
  425. // _SetCustomProperty
  426. //
  427. // Assign a custom property to the text covered by the selection.
  428. //----------------------------------------------------------------------------
  429. void CMarkTextService::_SetCustomProperty(TfEditCookie ec, ITfContext *pContext)
  430. {
  431. TF_SELECTION tfSelection;
  432. ITfProperty *pCustomProperty;
  433. CCustomPropertyStore *pCustomPropertyStore;
  434. ULONG cFetched;
  435. // get the case property
  436. if (pContext->GetProperty(c_guidCustomProperty, &pCustomProperty) != S_OK)
  437. return;
  438. // get the selection
  439. if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) != S_OK ||
  440. cFetched != 1)
  441. {
  442. // no selection or something went wrong
  443. tfSelection.range = NULL;
  444. goto Exit;
  445. }
  446. if ((pCustomPropertyStore = new CCustomPropertyStore) == NULL)
  447. goto Exit;
  448. pCustomProperty->SetValueStore(ec, tfSelection.range, pCustomPropertyStore);
  449. // TSF will hold a reference to pCustomPropertyStore is the SetValueStore succeeded
  450. // but we need to release ours
  451. pCustomPropertyStore->Release();
  452. Exit:
  453. pCustomProperty->Release();
  454. SafeRelease(tfSelection.range);
  455. }