ToolTipEx.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. #include "stdafx.h"
  2. #include "cp_main.h"
  3. #include "ToolTipEx.h"
  4. #include "BitmapHelper.h"
  5. #ifdef _DEBUG
  6. #define new DEBUG_NEW
  7. #undef THIS_FILE
  8. static char THIS_FILE[] = __FILE__;
  9. #endif
  10. #define DELETE_BITMAP if(m_pBitmap) \
  11. { \
  12. m_pBitmap->DeleteObject(); \
  13. delete m_pBitmap; \
  14. m_pBitmap = NULL; \
  15. }
  16. /////////////////////////////////////////////////////////////////////////////
  17. // CToolTipEx
  18. CToolTipEx::CToolTipEx(): m_dwTextStyle(DT_EXPANDTABS | DT_EXTERNALLEADING |
  19. DT_NOPREFIX | DT_WORDBREAK), m_rectMargin(2, 2, 3, 3),
  20. m_pBitmap(NULL), m_pNotifyWnd(NULL){}
  21. CToolTipEx::~CToolTipEx()
  22. {
  23. DELETE_BITMAP
  24. m_Font.DeleteObject();
  25. }
  26. BEGIN_MESSAGE_MAP(CToolTipEx, CWnd)
  27. //{{AFX_MSG_MAP(CToolTipEx)
  28. ON_WM_PAINT()
  29. ON_WM_SIZE()
  30. ON_WM_NCHITTEST()
  31. ON_WM_ACTIVATE()
  32. ON_WM_TIMER()
  33. ON_WM_NCPAINT()
  34. ON_WM_NCCALCSIZE()
  35. ON_WM_NCLBUTTONDOWN()
  36. ON_WM_NCMOUSEMOVE()
  37. ON_WM_NCLBUTTONUP()
  38. ON_WM_ERASEBKGND()
  39. END_MESSAGE_MAP()
  40. /////////////////////////////////////////////////////////////////////////////
  41. // CToolTipEx message handlers
  42. BOOL CToolTipEx::Create(CWnd *pParentWnd)
  43. {
  44. // Get the class name and create the window
  45. CString szClassName = AfxRegisterWndClass(CS_CLASSDC | CS_SAVEBITS,
  46. LoadCursor(NULL, IDC_ARROW));
  47. // Create the window - just don't show it yet.
  48. if( !CWnd::CreateEx(WS_EX_TOPMOST, szClassName, _T(""), WS_POPUP | WS_BORDER,
  49. 0, 0, 10, 10, // size & position updated when needed
  50. pParentWnd->GetSafeHwnd(), 0, NULL))
  51. {
  52. return FALSE;
  53. }
  54. m_DittoWindow.DoCreate(this);
  55. m_DittoWindow.SetCaptionColors(g_Opt.m_Theme.CaptionLeft(), g_Opt.m_Theme.CaptionRight());
  56. m_DittoWindow.SetCaptionOn(this, CGetSetOptions::GetCaptionPos(), true);
  57. m_DittoWindow.m_bDrawMinimize = false;
  58. m_DittoWindow.m_bDrawMinimize = false;
  59. m_DittoWindow.m_bDrawChevron = false;
  60. m_DittoWindow.m_sendWMClose = false;
  61. m_RichEdit.Create(_T(""), _T(""), WS_CHILD | WS_VISIBLE | WS_VSCROLL |
  62. WS_HSCROLL | ES_MULTILINE | ES_AUTOVSCROLL |
  63. ES_AUTOHSCROLL, CRect(10, 10, 100, 200), this, 1);
  64. m_RichEdit.SetReadOnly();
  65. m_RichEdit.SetBackgroundColor(FALSE, GetSysColor(COLOR_INFOBK));
  66. SetLogFont(GetSystemToolTipFont(), FALSE);
  67. return TRUE;
  68. }
  69. BOOL CToolTipEx::Show(CPoint point)
  70. {
  71. m_reducedWindowSize = false;
  72. if(m_pBitmap)
  73. {
  74. m_RichEdit.ShowWindow(SW_HIDE);
  75. }
  76. else
  77. {
  78. m_RichEdit.ShowWindow(SW_SHOW);
  79. }
  80. CRect rect = GetBoundsRect();
  81. //account for the scroll bars
  82. rect.right += 20;
  83. rect.bottom += 20;
  84. if (m_pBitmap)
  85. {
  86. int nWidth = CBitmapHelper::GetCBitmapWidth(*m_pBitmap);
  87. int nHeight = CBitmapHelper::GetCBitmapHeight(*m_pBitmap);
  88. rect.right = rect.left + nWidth;
  89. rect.bottom = rect.top + nHeight;
  90. }
  91. else if(m_csRTF != "")
  92. {
  93. //if showing rtf then increase the size because
  94. //rtf will probably draw bigger
  95. long lNewWidth = (long)rect.Width() + (long)(rect.Width() *.3);
  96. rect.right = rect.left + lNewWidth;
  97. long lNewHeight = rect.Height() + (rect.Height() *1);
  98. rect.bottom = rect.top + lNewHeight;
  99. }
  100. rect.right += CAPTION_BORDER * 2;
  101. rect.bottom += CAPTION_BORDER * 2;
  102. CRect rcScreen;
  103. ClientToScreen(rect);
  104. CRect cr(point, point);
  105. int nMonitor = GetMonitorFromRect(&cr);
  106. GetMonitorRect(nMonitor, &rcScreen);
  107. //ensure that we don't go outside the screen
  108. if(point.x < 0)
  109. {
  110. point.x = 5;
  111. m_reducedWindowSize = true;
  112. }
  113. if(point.y < 0)
  114. {
  115. point.y = 5;
  116. m_reducedWindowSize = true;
  117. }
  118. rcScreen.DeflateRect(0, 0, 5, 5);
  119. long lWidth = rect.Width();
  120. long lHeight = rect.Height();
  121. rect.left = point.x;
  122. rect.top = point.y;
  123. rect.right = rect.left + lWidth;
  124. rect.bottom = rect.top + lHeight;
  125. if(rect.right > rcScreen.right)
  126. {
  127. rect.right = rcScreen.right;
  128. m_reducedWindowSize = true;
  129. }
  130. if(rect.bottom > rcScreen.bottom)
  131. {
  132. rect.bottom = rcScreen.bottom;
  133. m_reducedWindowSize = true;
  134. }
  135. SetWindowPos(&CWnd::wndTopMost, point.x, point.y, rect.Width(), rect.Height
  136. (), SWP_SHOWWINDOW | SWP_NOCOPYBITS | SWP_NOACTIVATE |
  137. SWP_NOZORDER);
  138. return TRUE;
  139. }
  140. BOOL CToolTipEx::Hide()
  141. {
  142. DELETE_BITMAP
  143. ShowWindow(SW_HIDE);
  144. m_csRTF = "";
  145. m_csText = "";
  146. return TRUE;
  147. }
  148. void CToolTipEx::OnPaint()
  149. {
  150. CPaintDC dc(this); // device context for painting
  151. CRect rect;
  152. GetClientRect(rect);
  153. // Draw Text
  154. // dc.SetBkMode(TRANSPARENT);
  155. // rect.DeflateRect(m_rectMargin);
  156. if(m_pBitmap)
  157. {
  158. CBrush Brush, *pOldBrush;
  159. Brush.CreateSolidBrush(GetSysColor(COLOR_INFOBK));
  160. pOldBrush = dc.SelectObject(&Brush);
  161. CFont *pOldFont = dc.SelectObject(&m_Font);
  162. dc.FillRect(&rect, &Brush);
  163. CDC MemDc;
  164. MemDc.CreateCompatibleDC(&dc);
  165. CBitmap *oldBitmap = MemDc.SelectObject(m_pBitmap);
  166. int nWidth = CBitmapHelper::GetCBitmapWidth(*m_pBitmap);
  167. int nHeight = CBitmapHelper::GetCBitmapHeight(*m_pBitmap);
  168. if(m_reducedWindowSize)
  169. {
  170. dc.StretchBlt(rect.left, rect.top, rect.Width(), rect.Height(), &MemDc, 0, 0, nWidth, nWidth, SRCCOPY);
  171. }
  172. else
  173. {
  174. dc.BitBlt(rect.left, rect.top, nWidth, nHeight, &MemDc, 0, 0, SRCCOPY);
  175. }
  176. //dc.StretchBlt(rect.left, rect.top, rect.Width(), rect.Height(), &MemDc, 0, 0, nWidth, nHeight, SRCCOPY);
  177. MemDc.SelectObject(oldBitmap);
  178. rect.top += nHeight;
  179. }
  180. //dc.DrawText(m_csText, rect, m_dwTextStyle);
  181. // Cleanup
  182. // dc.SelectObject(pOldBrush);
  183. // dc.SelectObject(pOldFont);
  184. }
  185. void CToolTipEx::PostNcDestroy()
  186. {
  187. CWnd::PostNcDestroy();
  188. delete this;
  189. }
  190. BOOL CToolTipEx::PreTranslateMessage(MSG *pMsg)
  191. {
  192. m_DittoWindow.DoPreTranslateMessage(pMsg);
  193. switch(pMsg->message)
  194. {
  195. case WM_KEYDOWN:
  196. switch(pMsg->wParam)
  197. {
  198. case VK_ESCAPE:
  199. Hide();
  200. return TRUE;
  201. case 'C':
  202. if(GetKeyState(VK_CONTROL) &0x8000)
  203. {
  204. m_RichEdit.Copy();
  205. }
  206. break;
  207. }
  208. }
  209. return CWnd::PreTranslateMessage(pMsg);
  210. }
  211. BOOL CToolTipEx::OnMsg(MSG *pMsg)
  212. {
  213. if(FALSE == IsWindowVisible())
  214. {
  215. return FALSE;
  216. }
  217. switch(pMsg->message)
  218. {
  219. case WM_WINDOWPOSCHANGING:
  220. case WM_LBUTTONDOWN:
  221. {
  222. if(!IsCursorInToolTip())
  223. {
  224. Hide();
  225. }
  226. break;
  227. }
  228. case WM_KEYDOWN:
  229. {
  230. WPARAM vk = pMsg->wParam;
  231. if(vk == VK_ESCAPE)
  232. {
  233. Hide();
  234. return TRUE;
  235. }
  236. else if(vk == VK_TAB)
  237. {
  238. m_RichEdit.SetFocus();
  239. return TRUE;
  240. }
  241. else if(vk == 'N')
  242. {
  243. return FALSE;
  244. }
  245. else if (vk == 'P')
  246. {
  247. return FALSE;
  248. }
  249. Hide();
  250. break;
  251. }
  252. case WM_LBUTTONDBLCLK:
  253. case WM_RBUTTONDOWN:
  254. case WM_RBUTTONDBLCLK:
  255. case WM_MBUTTONDOWN:
  256. case WM_MBUTTONDBLCLK:
  257. case WM_NCLBUTTONDOWN:
  258. case WM_NCLBUTTONDBLCLK:
  259. case WM_NCRBUTTONDOWN:
  260. case WM_NCRBUTTONDBLCLK:
  261. case WM_NCMBUTTONDOWN:
  262. case WM_NCMBUTTONDBLCLK:
  263. {
  264. Hide();
  265. break;
  266. }
  267. }
  268. return FALSE;
  269. }
  270. CRect CToolTipEx::GetBoundsRect()
  271. {
  272. CWindowDC dc(NULL);
  273. CFont *pOldFont = (CFont*)dc.SelectObject((CFont*) &m_Font);
  274. int nLineWidth = 0;
  275. if(nLineWidth == 0)
  276. {
  277. // Count the number of lines of text
  278. int nStart = 0, nNumLines = 0;
  279. CString strTextCopy = m_csText;
  280. do
  281. {
  282. nStart = strTextCopy.Find(_T("\n"));
  283. // skip found character
  284. if(nStart >= 0)
  285. {
  286. strTextCopy = strTextCopy.Mid(nStart + 1);
  287. }
  288. nNumLines++;
  289. }
  290. while(nStart >= 0);
  291. // Find the widest line
  292. for(int i = 0; i < nNumLines; i++)
  293. {
  294. CString strLine = GetFieldFromString(m_csText, i, _T('\n')) + _T(
  295. " ");
  296. nLineWidth = max(nLineWidth, dc.GetTextExtent(strLine).cx);
  297. }
  298. }
  299. CRect rect(0, 0, max(0, nLineWidth), 0);
  300. dc.DrawText(m_csText, rect, DT_CALCRECT | m_dwTextStyle);
  301. dc.SelectObject(pOldFont);
  302. rect.bottom += m_rectMargin.top + m_rectMargin.bottom;
  303. rect.right += m_rectMargin.left + m_rectMargin.right + 2;
  304. if(m_pBitmap)
  305. {
  306. int nWidth = CBitmapHelper::GetCBitmapWidth(*m_pBitmap);
  307. int nHeight = CBitmapHelper::GetCBitmapHeight(*m_pBitmap);
  308. rect.bottom += nHeight;
  309. if((rect.left + nWidth) > rect.right)
  310. {
  311. rect.right = rect.left + nWidth;
  312. }
  313. }
  314. return rect;
  315. }
  316. CString CToolTipEx::GetFieldFromString(CString ref, int nIndex, TCHAR ch)
  317. {
  318. CString strReturn;
  319. LPCTSTR pstrStart = ref.LockBuffer();
  320. LPCTSTR pstrBuffer = pstrStart;
  321. int nCurrent = 0;
  322. int nStart = 0;
  323. int nEnd = 0;
  324. int nOldStart = 0;
  325. while(nCurrent <= nIndex && *pstrBuffer != _T('\0'))
  326. {
  327. if(*pstrBuffer == ch)
  328. {
  329. nOldStart = nStart;
  330. nStart = nEnd + 1;
  331. nCurrent++;
  332. }
  333. nEnd++;
  334. pstrBuffer++;
  335. }
  336. // May have reached the end of the string
  337. if(*pstrBuffer == _T('\0'))
  338. {
  339. nOldStart = nStart;
  340. nEnd++;
  341. }
  342. ref.UnlockBuffer();
  343. if(nCurrent < nIndex)
  344. {
  345. //TRACE1("Warning: GetStringField - Couldn't find field %d.\n", nIndex);
  346. return strReturn;
  347. }
  348. return ref.Mid(nOldStart, nEnd - nOldStart - 1);
  349. }
  350. LPLOGFONT CToolTipEx::GetSystemToolTipFont()
  351. {
  352. static LOGFONT LogFont;
  353. NONCLIENTMETRICS ncm;
  354. ncm.cbSize = sizeof(NONCLIENTMETRICS);
  355. if(!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS),
  356. &ncm, 0))
  357. {
  358. return FALSE;
  359. }
  360. memcpy(&LogFont, &(ncm.lfStatusFont), sizeof(LOGFONT));
  361. return &LogFont;
  362. }
  363. BOOL CToolTipEx::SetLogFont(LPLOGFONT lpLogFont, BOOL bRedraw /*=TRUE*/)
  364. {
  365. ASSERT(lpLogFont);
  366. if(!lpLogFont)
  367. {
  368. return FALSE;
  369. }
  370. LOGFONT LogFont;
  371. // Store font as the global default
  372. memcpy(&LogFont, lpLogFont, sizeof(LOGFONT));
  373. // Create the actual font object
  374. m_Font.DeleteObject();
  375. m_Font.CreateFontIndirect(&LogFont);
  376. if(bRedraw && ::IsWindow(GetSafeHwnd()))
  377. {
  378. Invalidate();
  379. }
  380. return TRUE;
  381. }
  382. void CToolTipEx::SetBitmap(CBitmap *pBitmap)
  383. {
  384. DELETE_BITMAP
  385. m_pBitmap = pBitmap;
  386. }
  387. void CToolTipEx::OnSize(UINT nType, int cx, int cy)
  388. {
  389. CWnd::OnSize(nType, cx, cy);
  390. if(::IsWindow(m_RichEdit.GetSafeHwnd()) == FALSE)
  391. {
  392. return ;
  393. }
  394. m_DittoWindow.DoSetRegion(this);
  395. CRect cr;
  396. GetClientRect(cr);
  397. // cr.DeflateRect(0, 0, 15, 0);
  398. m_RichEdit.MoveWindow(cr);
  399. this->Invalidate();
  400. }
  401. BOOL CToolTipEx::IsCursorInToolTip()
  402. {
  403. CRect cr;
  404. GetWindowRect(cr);
  405. CPoint cursorPos;
  406. GetCursorPos(&cursorPos);
  407. return cr.PtInRect(cursorPos);
  408. }
  409. void CToolTipEx::SetRTFText(const char *pRTF)
  410. {
  411. m_RichEdit.SetRTF(pRTF);
  412. m_csRTF = pRTF;
  413. }
  414. //void CToolTipEx::SetRTFText(const CString &csRTF)
  415. //{
  416. // m_RichEdit.SetRTF(csRTF);
  417. // m_csRTF = csRTF;
  418. //}
  419. void CToolTipEx::SetToolTipText(const CString &csText)
  420. {
  421. m_csText = csText;
  422. m_RichEdit.SetFont(&m_Font);
  423. m_RichEdit.SetText(csText);
  424. }
  425. void CToolTipEx::OnActivate(UINT nState, CWnd *pWndOther, BOOL bMinimized)
  426. {
  427. CWnd::OnActivate(nState, pWndOther, bMinimized);
  428. if(nState == WA_INACTIVE)
  429. {
  430. Hide();
  431. if(m_pNotifyWnd)
  432. {
  433. m_pNotifyWnd->PostMessage(NM_INACTIVE_TOOLTIPWND, 0, 0);
  434. }
  435. }
  436. }
  437. void CToolTipEx::OnTimer(UINT_PTR nIDEvent)
  438. {
  439. switch(nIDEvent)
  440. {
  441. case HIDE_WINDOW_TIMER:
  442. Hide();
  443. PostMessage(WM_DESTROY, 0, 0);
  444. break;
  445. }
  446. CWnd::OnTimer(nIDEvent);
  447. }
  448. void CToolTipEx::OnNcPaint()
  449. {
  450. m_DittoWindow.DoNcPaint(this);
  451. }
  452. void CToolTipEx::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp)
  453. {
  454. CWnd::OnNcCalcSize(bCalcValidRects, lpncsp);
  455. m_DittoWindow.DoNcCalcSize(bCalcValidRects, lpncsp);
  456. }
  457. HITTEST_RET CToolTipEx::OnNcHitTest(CPoint point)
  458. {
  459. UINT Ret = m_DittoWindow.DoNcHitTest(this, point);
  460. if(Ret == -1)
  461. return CWnd::OnNcHitTest(point);
  462. return Ret;
  463. }
  464. void CToolTipEx::OnNcLButtonDown(UINT nHitTest, CPoint point)
  465. {
  466. m_DittoWindow.DoNcLButtonDown(this, nHitTest, point);
  467. CWnd::OnNcLButtonDown(nHitTest, point);
  468. }
  469. void CToolTipEx::OnNcLButtonUp(UINT nHitTest, CPoint point)
  470. {
  471. long lRet = m_DittoWindow.DoNcLButtonUp(this, nHitTest, point);
  472. switch(lRet)
  473. {
  474. case BUTTON_CLOSE:
  475. Hide();
  476. break;
  477. }
  478. CWnd::OnNcLButtonUp(nHitTest, point);
  479. }
  480. void CToolTipEx::OnNcMouseMove(UINT nHitTest, CPoint point)
  481. {
  482. m_DittoWindow.DoNcMouseMove(this, nHitTest, point);
  483. CWnd::OnNcMouseMove(nHitTest, point);
  484. }