TitleTip.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. #include "stdafx.h"
  2. #include "gridctrl.h"
  3. #ifndef GRIDCONTROL_NO_TITLETIPS
  4. #include "TitleTip.h"
  5. #ifdef _DEBUG
  6. #define new DEBUG_NEW
  7. #undef THIS_FILE
  8. static char THIS_FILE[] = __FILE__;
  9. #endif
  10. CTitleTip::CTitleTip()
  11. {
  12. // Register the window class if it has not already been registered.
  13. WNDCLASS wndcls;
  14. HINSTANCE hInst = AfxGetInstanceHandle();
  15. if (!(::GetClassInfo(hInst, TITLETIP_CLASSNAME, &wndcls)))
  16. {
  17. // otherwise we need to register a new class
  18. wndcls.style = CS_SAVEBITS;
  19. wndcls.lpfnWndProc = ::DefWindowProc;
  20. wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
  21. wndcls.hInstance = hInst;
  22. wndcls.hIcon = NULL;
  23. wndcls.hCursor = LoadCursor(hInst, IDC_ARROW);
  24. wndcls.hbrBackground = (HBRUSH)(COLOR_INFOBK + 1);
  25. wndcls.lpszMenuName = NULL;
  26. wndcls.lpszClassName = TITLETIP_CLASSNAME;
  27. if (!AfxRegisterClass(&wndcls))
  28. AfxThrowResourceException();
  29. }
  30. m_dwLastLButtonDown = ULONG_MAX;
  31. m_dwDblClickMsecs = GetDoubleClickTime();
  32. m_bCreated = FALSE;
  33. m_pParentWnd = NULL;
  34. }
  35. CTitleTip::~CTitleTip()
  36. {
  37. }
  38. BEGIN_MESSAGE_MAP(CTitleTip, CWnd)
  39. //{{AFX_MSG_MAP(CTitleTip)
  40. ON_WM_MOUSEMOVE()
  41. //}}AFX_MSG_MAP
  42. END_MESSAGE_MAP()
  43. /////////////////////////////////////////////////////////////////////////////
  44. // CTitleTip message handlers
  45. BOOL CTitleTip::Create(CWnd * pParentWnd)
  46. {
  47. ASSERT_VALID(pParentWnd);
  48. // Already created?
  49. if (m_bCreated)
  50. return TRUE;
  51. DWORD dwStyle = WS_BORDER | WS_POPUP;
  52. DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
  53. m_pParentWnd = pParentWnd;
  54. m_bCreated = CreateEx(dwExStyle, TITLETIP_CLASSNAME, NULL, dwStyle,
  55. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  56. NULL, NULL, NULL);
  57. return m_bCreated;
  58. }
  59. BOOL CTitleTip::DestroyWindow()
  60. {
  61. m_bCreated = FALSE;
  62. return CWnd::DestroyWindow();
  63. }
  64. // Show - Show the titletip if needed
  65. // rectTitle - The rectangle within which the original
  66. // title is constrained - in client coordinates
  67. // lpszTitleText - The text to be displayed
  68. // xoffset - Number of pixel that the text is offset from
  69. // left border of the cell
  70. void CTitleTip::Show(CRect rectTitle, LPCTSTR lpszTitleText, int xoffset /*=0*/,
  71. LPRECT lpHoverRect /*=NULL*/,
  72. const LOGFONT* lpLogFont /*=NULL*/,
  73. COLORREF crTextClr /* CLR_DEFAULT */,
  74. COLORREF crBackClr /* CLR_DEFAULT */)
  75. {
  76. if (!IsWindow(m_hWnd))
  77. Create(m_pParentWnd);
  78. ASSERT(::IsWindow(GetSafeHwnd()));
  79. if (rectTitle.IsRectEmpty())
  80. return;
  81. // If titletip is already displayed, don't do anything.
  82. if (IsWindowVisible())
  83. return;
  84. m_rectHover = (lpHoverRect != NULL) ? lpHoverRect : rectTitle;
  85. m_rectHover.right++; m_rectHover.bottom++;
  86. m_pParentWnd->ClientToScreen(m_rectHover);
  87. ScreenToClient(m_rectHover);
  88. // Do not display the titletip is app does not have focus
  89. if (GetFocus() == NULL)
  90. return;
  91. // Define the rectangle outside which the titletip will be hidden.
  92. // We add a buffer of one pixel around the rectangle
  93. m_rectTitle.top = -1;
  94. m_rectTitle.left = -xoffset - 1;
  95. m_rectTitle.right = rectTitle.Width() - xoffset;
  96. m_rectTitle.bottom = rectTitle.Height() + 1;
  97. // Determine the width of the text
  98. m_pParentWnd->ClientToScreen(rectTitle);
  99. CClientDC dc(this);
  100. CString strTitle = _T("");
  101. strTitle += _T(" ");
  102. strTitle += lpszTitleText;
  103. strTitle += _T(" ");
  104. CFont font, *pOldFont = NULL;
  105. if (lpLogFont)
  106. {
  107. font.CreateFontIndirect(lpLogFont);
  108. pOldFont = dc.SelectObject(&font);
  109. }
  110. else
  111. {
  112. // use same font as ctrl
  113. pOldFont = dc.SelectObject(m_pParentWnd->GetFont());
  114. }
  115. CSize size = dc.GetTextExtent(strTitle);
  116. TEXTMETRIC tm;
  117. dc.GetTextMetrics(&tm);
  118. size.cx += tm.tmOverhang;
  119. CRect rectDisplay = rectTitle;
  120. rectDisplay.left += xoffset;
  121. rectDisplay.right = rectDisplay.left + size.cx + xoffset;
  122. // Do not display if the text fits within available space
  123. if (rectDisplay.right > rectTitle.right - xoffset)
  124. {
  125. // Show the titletip
  126. SetWindowPos(&wndTop, rectDisplay.left, rectDisplay.top,
  127. rectDisplay.Width(), rectDisplay.Height(),
  128. SWP_SHOWWINDOW | SWP_NOACTIVATE);
  129. // FNA - handle colors correctly
  130. if (crBackClr != CLR_DEFAULT)
  131. {
  132. CBrush backBrush(crBackClr);
  133. CBrush* pOldBrush = dc.SelectObject(&backBrush);
  134. CRect rect;
  135. dc.GetClipBox(&rect); // Erase the area needed
  136. dc.PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY);
  137. dc.SelectObject(pOldBrush);
  138. }
  139. // Set color
  140. if (crTextClr != CLR_DEFAULT)//FNA
  141. dc.SetTextColor(crTextClr);//FA
  142. dc.SetBkMode(TRANSPARENT);
  143. dc.TextOut(0, 0, strTitle);
  144. SetCapture();
  145. }
  146. dc.SelectObject(pOldFont);
  147. }
  148. void CTitleTip::Hide()
  149. {
  150. if (!::IsWindow(GetSafeHwnd()))
  151. return;
  152. if (GetCapture()->GetSafeHwnd() == GetSafeHwnd())
  153. ReleaseCapture();
  154. ShowWindow(SW_HIDE);
  155. }
  156. void CTitleTip::OnMouseMove(UINT nFlags, CPoint point)
  157. {
  158. if (!m_rectHover.PtInRect(point))
  159. {
  160. Hide();
  161. // Forward the message
  162. ClientToScreen(&point);
  163. CWnd *pWnd = WindowFromPoint(point);
  164. if (pWnd == this)
  165. pWnd = m_pParentWnd;
  166. int hittest = (int)pWnd->SendMessage(WM_NCHITTEST, 0, MAKELONG(point.x, point.y));
  167. if (hittest == HTCLIENT) {
  168. pWnd->ScreenToClient(&point);
  169. pWnd->PostMessage(WM_MOUSEMOVE, nFlags, MAKELONG(point.x, point.y));
  170. }
  171. else {
  172. pWnd->PostMessage(WM_NCMOUSEMOVE, hittest, MAKELONG(point.x, point.y));
  173. }
  174. }
  175. }
  176. BOOL CTitleTip::PreTranslateMessage(MSG* pMsg)
  177. {
  178. // Used to qualify WM_LBUTTONDOWN messages as double-clicks
  179. DWORD dwTick = 0;
  180. BOOL bDoubleClick = FALSE;
  181. CWnd *pWnd;
  182. int hittest;
  183. switch (pMsg->message)
  184. {
  185. case WM_LBUTTONDOWN:
  186. // Get tick count since last LButtonDown
  187. dwTick = GetTickCount();
  188. bDoubleClick = ((dwTick - m_dwLastLButtonDown) <= m_dwDblClickMsecs);
  189. m_dwLastLButtonDown = dwTick;
  190. // NOTE: DO NOT ADD break; STATEMENT HERE! Let code fall through
  191. case WM_RBUTTONDOWN:
  192. case WM_MBUTTONDOWN:
  193. {
  194. POINTS pts = MAKEPOINTS(pMsg->lParam);
  195. POINT point;
  196. point.x = pts.x;
  197. point.y = pts.y;
  198. ClientToScreen(&point);
  199. Hide();
  200. pWnd = WindowFromPoint(point);
  201. if (!pWnd)
  202. return CWnd::PreTranslateMessage(pMsg);
  203. if (pWnd->GetSafeHwnd() == GetSafeHwnd())
  204. pWnd = m_pParentWnd;
  205. hittest = (int)pWnd->SendMessage(WM_NCHITTEST, 0, MAKELONG(point.x, point.y));
  206. if (hittest == HTCLIENT)
  207. {
  208. pWnd->ScreenToClient(&point);
  209. pMsg->lParam = MAKELONG(point.x, point.y);
  210. }
  211. else
  212. {
  213. switch (pMsg->message) {
  214. case WM_LBUTTONDOWN:
  215. pMsg->message = WM_NCLBUTTONDOWN;
  216. break;
  217. case WM_RBUTTONDOWN:
  218. pMsg->message = WM_NCRBUTTONDOWN;
  219. break;
  220. case WM_MBUTTONDOWN:
  221. pMsg->message = WM_NCMBUTTONDOWN;
  222. break;
  223. }
  224. pMsg->wParam = hittest;
  225. pMsg->lParam = MAKELONG(point.x, point.y);
  226. }
  227. // If this is the 2nd WM_LBUTTONDOWN in x milliseconds,
  228. // post a WM_LBUTTONDBLCLK message instead of a single click.
  229. pWnd->PostMessage(bDoubleClick ? WM_LBUTTONDBLCLK : pMsg->message,
  230. pMsg->wParam,
  231. pMsg->lParam);
  232. return TRUE;
  233. }
  234. case WM_KEYDOWN:
  235. case WM_SYSKEYDOWN:
  236. Hide();
  237. m_pParentWnd->PostMessage(pMsg->message, pMsg->wParam, pMsg->lParam);
  238. return TRUE;
  239. }
  240. if (GetFocus() == NULL)
  241. {
  242. Hide();
  243. return TRUE;
  244. }
  245. return CWnd::PreTranslateMessage(pMsg);
  246. }
  247. #endif // GRIDCONTROL_NO_TITLETIPS