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.

500 lines
12 KiB

  1. #ifndef __tmplrEdit_h
  2. #define __tmplrEdit_h
  3. #include <atlctrls.h>
  4. #include <winuser.h>
  5. /////////////////////////////////////////////////////////////////////////////
  6. // CWindowImplHotlinkRichEdit
  7. // Purpose - To display a hyperlink control (like Syslink in Whistler) using a rich edit ctrl
  8. //
  9. // Usage - CWindowImplHotlinkRichEdit<> m_Hotlink;
  10. // CDialog::OnInitDialog(..)
  11. // {
  12. // ...
  13. // m_Hotlink.SubClassWindow( GetDlgItem( IDC_RICHEDIT1 ));
  14. // ::SendMessage (
  15. // GetDlgItem(IDC_RICHEDIT1), WM_SETTEXT, 0 ,
  16. // (LPARAM) _T("Click <A>here</A> to do something")
  17. // );
  18. // ...
  19. // }
  20. #define LINKSTARTTAG _T("<A>")
  21. #define LINKENDTAG _T("</A>")
  22. template <class T = CRichEditCtrl, class TBase = CWindow, class TWinTraits = CControlWinTraits>
  23. class CWindowImplHotlinkRichEdit : public CWindowImpl< T, TBase, TWinTraits >
  24. {
  25. private:
  26. int m_iLinkIndex;
  27. RECT m_rect;
  28. BOOL m_bHasFocus;
  29. public:
  30. CWindowImplHotlinkRichEdit() : m_iLinkIndex(-1), m_bHasFocus(FALSE)
  31. {
  32. ZeroMemory(&m_rect, sizeof(m_rect));
  33. }
  34. BEGIN_MSG_MAP(CWindowImplHotlinkRichEdit)
  35. MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
  36. MESSAGE_HANDLER(WM_SETTEXT, OnSetText)
  37. MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
  38. MESSAGE_HANDLER(WM_PAINT, OnPaint)
  39. MESSAGE_HANDLER(WM_CHAR, OnChar)
  40. MESSAGE_HANDLER(WM_NCHITTEST, OnHitTest)
  41. MESSAGE_HANDLER(WM_KEYDOWN, OnKey)
  42. MESSAGE_HANDLER(WM_KEYUP, OnKey)
  43. END_MSG_MAP()
  44. HFONT CharFormatToHFont(CHARFORMAT * pcf)
  45. {
  46. // Create a font that matches the font specified by pcf
  47. HFONT hFont = NULL;
  48. HDC hDC = GetDC();
  49. if (pcf)
  50. {
  51. LOGFONT lf;
  52. ZeroMemory(&lf, sizeof(lf));
  53. lf.lfCharSet = pcf->bCharSet;
  54. lf.lfPitchAndFamily = pcf->bPitchAndFamily;
  55. // yHeight is in twips.
  56. lf.lfHeight = -1 * pcf->yHeight / 20.0 * GetDeviceCaps (hDC, LOGPIXELSY) / 72;
  57. _tcsncpy(lf.lfFaceName, pcf->szFaceName, LF_FACESIZE);
  58. lf.lfFaceName[LF_FACESIZE-1] = NULL;
  59. hFont = CreateFontIndirect(&lf);
  60. }
  61. if (hDC)
  62. {
  63. ReleaseDC(hDC);
  64. }
  65. return hFont;
  66. }
  67. BOOL HFontToCharFormat(HFONT hFont, CHARFORMAT * pcf)
  68. {
  69. BOOL bRet = FALSE;
  70. HDC hDC = GetDC();
  71. if (hFont)
  72. {
  73. LOGFONT lf;
  74. ZeroMemory(&lf, sizeof(lf));
  75. if (GetObject(hFont, sizeof(lf), &lf))
  76. {
  77. pcf->bCharSet = lf.lfCharSet;
  78. pcf->bPitchAndFamily = lf.lfPitchAndFamily;
  79. // yHeight is in twips
  80. pcf->yHeight = 20 * lf.lfHeight * 72.0 / GetDeviceCaps (hDC, LOGPIXELSY);
  81. pcf->yHeight = pcf->yHeight < 0 ? -pcf->yHeight : pcf->yHeight;
  82. _tcsncpy(pcf->szFaceName, lf.lfFaceName, LF_FACESIZE);
  83. pcf->szFaceName[LF_FACESIZE-1] = NULL;
  84. pcf->dwMask |= CFM_CHARSET | CFM_FACE | CFM_SIZE;
  85. bRet = TRUE;
  86. }
  87. }
  88. if (hDC)
  89. {
  90. ReleaseDC(hDC);
  91. }
  92. return bRet;
  93. }
  94. BOOL SubclassWindow(HWND hWnd)
  95. {
  96. BOOL bRC = FALSE;
  97. if (::IsWindow(hWnd))
  98. {
  99. bRC = CWindowImpl< T, TBase, TWinTraits >::SubclassWindow( hWnd );
  100. ::SendMessage(hWnd, EM_SETSEL, -1, 0);
  101. }
  102. return bRC;
  103. }
  104. LRESULT OnPaint( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
  105. {
  106. DefWindowProc(uMsg, wParam, lParam);
  107. ::SendMessage(m_hWnd, EM_SETSEL, -1, 0);
  108. HideCaret();
  109. if (m_bHasFocus)
  110. {
  111. DrawHotlinkFocusRect();
  112. }
  113. bHandled = TRUE;
  114. return 0;
  115. }
  116. LRESULT OnKey( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
  117. {
  118. if (VK_TAB == wParam || VK_SHIFT == wParam || VK_ESCAPE == wParam)
  119. {
  120. return DefWindowProc(uMsg, wParam, lParam);
  121. }
  122. bHandled = TRUE;
  123. return 0;
  124. }
  125. LRESULT OnChar( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
  126. {
  127. if (VK_RETURN == wParam || VK_SPACE == wParam) // Enter and space when we have the focus...
  128. {
  129. HWND hWndParent;
  130. hWndParent = ::GetParent(m_hWnd);
  131. if (hWndParent)
  132. {
  133. NMHDR nmhdr;
  134. ENLINK enlink;
  135. nmhdr.hwndFrom = m_hWnd;
  136. nmhdr.idFrom = ::GetDlgCtrlID(m_hWnd);
  137. nmhdr.code = EN_LINK;
  138. enlink.msg = uMsg;
  139. enlink.lParam = lParam;
  140. enlink.wParam = wParam;
  141. enlink.nmhdr = nmhdr;
  142. // DO NOT USE PostMessage for the notification, can cause AV
  143. ::SendMessage(hWndParent, WM_NOTIFY, ::GetDlgCtrlID(m_hWnd), (LPARAM) &enlink);
  144. }
  145. }
  146. bHandled = TRUE;
  147. return 0;
  148. }
  149. LRESULT OnHitTest( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
  150. {
  151. POINTS pts;
  152. POINT pt;
  153. pts = MAKEPOINTS(lParam);
  154. POINTSTOPOINT(pt, pts);
  155. ::MapWindowPoints(NULL, m_hWnd, &pt, 1);
  156. if (PtInRect(&m_rect, pt))
  157. {
  158. return DefWindowProc(uMsg, wParam, lParam);
  159. }
  160. return HTTRANSPARENT;
  161. }
  162. LRESULT OnKillFocus( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
  163. {
  164. DefWindowProc(uMsg, wParam, lParam);
  165. if (TRUE == m_bHasFocus)
  166. {
  167. DrawHotlinkFocusRect();
  168. m_bHasFocus = FALSE;
  169. }
  170. bHandled = TRUE;
  171. return 0;
  172. }
  173. LRESULT OnFocus( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
  174. {
  175. // Each time we get WM_SETFOCUS we need to draw focus rect
  176. // around the link
  177. DefWindowProc(uMsg, wParam, lParam);
  178. ::SendMessage(m_hWnd, EM_SETSEL, -1, 0);
  179. HideCaret();
  180. if (FALSE == m_bHasFocus)
  181. {
  182. DrawHotlinkFocusRect();
  183. m_bHasFocus = TRUE;
  184. }
  185. bHandled = TRUE;
  186. return 0;
  187. }
  188. LRESULT OnSetText( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
  189. {
  190. TCHAR szLink[128];
  191. TCHAR * szText = reinterpret_cast<LPTSTR> (lParam);
  192. TCHAR * szActualText = NULL;
  193. TCHAR * pLinkStart, * pLinkEnd, * pTemp;
  194. int i,j;
  195. // When we get WM_SETTEXT message, we will search the text for
  196. // the link identified by <A> </A>
  197. // After identifying the link we will strip off the tags and send the message
  198. // to DefWindowProc
  199. // Ex: "Click <A>here</A> to do something interesting"
  200. if (szText)
  201. {
  202. pLinkStart = _tcsstr(szText, LINKSTARTTAG);
  203. pLinkEnd = _tcsstr(szText, LINKENDTAG);
  204. // Make sure that we have a link in the text
  205. if (pLinkStart && pLinkEnd && pLinkStart < pLinkEnd)
  206. {
  207. // szActualText will hold the final text without the tags
  208. szActualText = new TCHAR[_tcslen(szText) + 1];
  209. if (szActualText)
  210. {
  211. i = j = 0;
  212. pTemp = pLinkStart + _tcslen(LINKSTARTTAG); // pTemp = "here</A> to do something interesting"
  213. while (pTemp < pLinkEnd)
  214. {
  215. szLink[i++] = *pTemp++;
  216. }
  217. szLink[i] = NULL; // szLink = "here"
  218. while(szText < pLinkStart)
  219. {
  220. szActualText[j++] = *szText++;
  221. }
  222. szActualText[j] = NULL; // szActualText = "Click"
  223. m_iLinkIndex = j;
  224. _tcscat(szActualText, szLink); // szActualText = "Click here"
  225. pTemp = pLinkEnd + _tcslen(LINKENDTAG); // pTemp = " to do something interesting"
  226. _tcscat(szActualText, pTemp); // szActualText = "Click here to do something interesting"
  227. }
  228. }
  229. }
  230. bHandled = TRUE;
  231. if (szActualText)
  232. {
  233. HWND hWndParent;
  234. HFONT hFont;
  235. CHARFORMAT cf;
  236. ZeroMemory(&cf, sizeof(cf));
  237. cf.cbSize = sizeof(cf);
  238. hWndParent = ::GetParent(m_hWnd);
  239. if (hWndParent)
  240. {
  241. // Stick to parent window's font...
  242. hFont = reinterpret_cast<HFONT> (::SendMessage(hWndParent, WM_GETFONT, 0, 0));
  243. if (hFont)
  244. {
  245. if (HFontToCharFormat(hFont, &cf))
  246. {
  247. ::SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf);
  248. }
  249. }
  250. }
  251. // Let the control display the text without the links
  252. DefWindowProc(uMsg, wParam, (LPARAM) szActualText);
  253. // Get current char format
  254. ::SendMessage(m_hWnd, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cf);
  255. cf.dwEffects |= CFE_LINK; // For link style
  256. // Select the link text
  257. ::SendMessage(m_hWnd, EM_SETSEL, m_iLinkIndex, m_iLinkIndex + _tcslen(szLink));
  258. // Change the format of the link text
  259. ::SendMessage(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf);
  260. // Get the rect that covers the link in logical units
  261. // We will use this rect to draw focus rect around the link
  262. GetLinkRect(szActualText, szLink);
  263. return 0;
  264. }
  265. else
  266. {
  267. return DefWindowProc(uMsg, wParam, lParam);
  268. }
  269. }
  270. BOOL GetLinkRect(LPTSTR szText, LPCTSTR szLink)
  271. {
  272. if (!szText || !szLink)
  273. {
  274. return FALSE;
  275. }
  276. BOOL bSuccess = FALSE;
  277. if (-1 != m_iLinkIndex)
  278. {
  279. DWORD dwStart;
  280. DWORD dwEnd;
  281. dwStart = ::SendMessage(m_hWnd, EM_POSFROMCHAR, m_iLinkIndex, 0);
  282. dwEnd = ::SendMessage(m_hWnd, EM_POSFROMCHAR, m_iLinkIndex + _tcslen(szLink), 0);
  283. CHARFORMAT cf;
  284. ZeroMemory(&cf, sizeof(cf));
  285. cf.cbSize = sizeof(cf);
  286. cf.dwMask |= CFM_CHARSET | CFM_FACE | CFM_SIZE;
  287. ::SendMessage(m_hWnd, EM_SETSEL, 0, -1);
  288. ::SendMessage(m_hWnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf);
  289. HFONT hFont = CharFormatToHFont(&cf);
  290. if (hFont)
  291. {
  292. HDC hDC = GetDC();
  293. if (hDC)
  294. {
  295. SelectObject(hDC, hFont);
  296. TEXTMETRIC tm;
  297. ZeroMemory(&tm, sizeof(tm));
  298. if (GetTextMetrics(hDC, &tm))
  299. {
  300. m_rect.left = LOWORD(dwStart);
  301. m_rect.top = HIWORD(dwStart);
  302. m_rect.right = LOWORD(dwEnd);
  303. m_rect.bottom = m_rect.top + tm.tmHeight + tm.tmDescent;
  304. bSuccess = TRUE;
  305. }
  306. ReleaseDC(hDC);
  307. }
  308. DeleteObject(hFont);
  309. }
  310. }
  311. return bSuccess;
  312. }
  313. BOOL DrawHotlinkFocusRect()
  314. {
  315. BOOL bRet = FALSE;
  316. if (-1 != m_iLinkIndex)
  317. {
  318. HDC hdc = GetDC();
  319. bRet = DrawFocusRect(hdc, &m_rect);
  320. ReleaseDC(hdc);
  321. }
  322. return bRet;
  323. }
  324. };
  325. #endif // #ifndef __tmplEdit.h