// ToolTipEx.cpp : implementation file // #include "stdafx.h" #include "cp_main.h" #include "ToolTipEx.h" #include "BitmapHelper.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define DELETE_BITMAP if(m_pBitmap) \ { \ m_pBitmap->DeleteObject(); \ DELETE_PTR(m_pBitmap); \ } ///////////////////////////////////////////////////////////////////////////// // CToolTipEx CToolTipEx::CToolTipEx() : m_dwTextStyle(DT_EXPANDTABS|DT_EXTERNALLEADING|DT_NOPREFIX|DT_WORDBREAK), m_rectMargin(2, 2, 3, 3), m_pBitmap(NULL), m_pNotifyWnd(NULL) { } CToolTipEx::~CToolTipEx() { DELETE_BITMAP m_Font.DeleteObject(); } BEGIN_MESSAGE_MAP(CToolTipEx, CWnd) //{{AFX_MSG_MAP(CToolTipEx) ON_WM_PAINT() ON_WM_SIZE() ON_WM_NCHITTEST() ON_WM_ACTIVATE() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CToolTipEx message handlers BOOL CToolTipEx::Create(CWnd* pParentWnd) { // Get the class name and create the window CString szClassName = AfxRegisterWndClass(CS_CLASSDC|CS_SAVEBITS, LoadCursor(NULL, IDC_ARROW)); // Create the window - just don't show it yet. if (!CWnd::CreateEx(WS_EX_TOPMOST, szClassName, _T(""), WS_POPUP|WS_BORDER, 0, 0, 10, 10, // size & position updated when needed pParentWnd->GetSafeHwnd(), 0, NULL)) { return FALSE; } m_RichEdit.Create(WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_HSCROLL|ES_MULTILINE|ES_AUTOVSCROLL|ES_AUTOHSCROLL, CRect(10,10,100,200), this, 1); m_RichEdit.SetReadOnly(); m_RichEdit.SetBackgroundColor(FALSE, GetSysColor(COLOR_INFOBK)); SetLogFont(GetSystemToolTipFont(), FALSE); return TRUE; } BOOL CToolTipEx::Show(CPoint point) { if(m_pBitmap) { m_RichEdit.ShowWindow(SW_HIDE); } else { m_RichEdit.ShowWindow(SW_SHOW); } CRect rect = GetBoundsRect(); //account for the scroll bars rect.right += 20; rect.bottom += 20; //if showing rtf then increase the size because //rtf will probably draw bigger if(m_csRTF != "") { long lNewWidth = rect.Width() + (rect.Width() * .3); rect.right = rect.left + lNewWidth; long lNewHeight = rect.Height() + (rect.Height() * 1); rect.bottom = rect.top + lNewHeight; } CRect rcScreen; ClientToScreen(rect); int nMonitor = GetMonitorFromRect(&rect); GetMonitorRect(nMonitor, &rcScreen); //ensure that we don't go outside the screen if(point.x < 0) point.x = 5; if(point.y < 0) point.y = 5; rcScreen.DeflateRect(0, 0, 5, 5); long lWidth = rect.Width(); long lHeight = rect.Height(); rect.left = point.x; rect.top = point.y; rect.right = rect.left + lWidth; rect.bottom = rect.top + lHeight; if(rect.right > rcScreen.right) rect.right = rcScreen.right; if(rect.bottom > rcScreen.bottom) rect.bottom = rcScreen.bottom; SetWindowPos(NULL, point.x, point.y, rect.Width(), rect.Height(), SWP_SHOWWINDOW|SWP_NOCOPYBITS|SWP_NOACTIVATE|SWP_NOZORDER); return TRUE; } BOOL CToolTipEx::Hide() { DELETE_BITMAP ShowWindow(SW_HIDE); m_csRTF = ""; m_csText = ""; return TRUE; } void CToolTipEx::OnPaint() { CPaintDC dc(this); // device context for painting CRect rect; GetClientRect(rect); // CBrush Brush, *pOldBrush; // Brush.CreateSolidBrush(GetSysColor(COLOR_INFOBK)); // pOldBrush = dc.SelectObject(&Brush); // CFont *pOldFont = dc.SelectObject(&m_Font); // dc.FillRect(&rect, &Brush); // Draw Text // dc.SetBkMode(TRANSPARENT); // rect.DeflateRect(m_rectMargin); if(m_pBitmap) { CDC MemDc; MemDc.CreateCompatibleDC(&dc); CBitmap* oldBitmap = MemDc.SelectObject(m_pBitmap); int nWidth = CBitmapHelper::GetCBitmapWidth(*m_pBitmap); int nHeight = CBitmapHelper::GetCBitmapHeight(*m_pBitmap); dc.BitBlt(rect.left, rect.top, nWidth, nHeight, &MemDc, 0, 0, SRCCOPY); MemDc.SelectObject(oldBitmap); rect.top += nHeight; } //dc.DrawText(m_csText, rect, m_dwTextStyle); // Cleanup // dc.SelectObject(pOldBrush); // dc.SelectObject(pOldFont); } void CToolTipEx::PostNcDestroy() { CWnd::PostNcDestroy(); delete this; } BOOL CToolTipEx::PreTranslateMessage(MSG* pMsg) { switch(pMsg->message) { case WM_KEYDOWN: switch( pMsg->wParam ) { case VK_ESCAPE: Hide(); return TRUE; case 'C': if(GetKeyState(VK_CONTROL) & 0x8000) { m_RichEdit.Copy(); } break; } } return CWnd::PreTranslateMessage(pMsg); } BOOL CToolTipEx::OnMsg(MSG* pMsg) { if(FALSE == IsWindowVisible()) { return FALSE; } switch(pMsg->message) { case WM_WINDOWPOSCHANGING: case WM_LBUTTONDOWN: { if (!IsCursorInToolTip()) Hide(); break; } case WM_KEYDOWN: { WPARAM vk = pMsg->wParam; if(vk == VK_ESCAPE) { Hide(); return TRUE; } else if(vk == VK_TAB) { m_RichEdit.SetFocus(); return TRUE; } Hide(); break; } case WM_LBUTTONDBLCLK: case WM_RBUTTONDOWN : case WM_RBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: case WM_NCLBUTTONDOWN: case WM_NCLBUTTONDBLCLK: case WM_NCRBUTTONDOWN: case WM_NCRBUTTONDBLCLK: case WM_NCMBUTTONDOWN: case WM_NCMBUTTONDBLCLK: { Hide(); break; } } return FALSE; } CRect CToolTipEx::GetBoundsRect() { CWindowDC dc(NULL); CFont *pOldFont = (CFont*) dc.SelectObject((CFont*)&m_Font); int nLineWidth = 0; if (nLineWidth == 0) { // Count the number of lines of text int nStart = 0, nNumLines = 0; CString strTextCopy = m_csText; do { nStart = strTextCopy.Find(_T("\n")); // skip found character if (nStart >= 0) strTextCopy = strTextCopy.Mid(nStart+1); nNumLines++; } while (nStart >= 0); // Find the widest line for (int i = 0; i < nNumLines; i++) { CString strLine = GetFieldFromString(m_csText, i, _T('\n')) + _T(" "); nLineWidth = max(nLineWidth, dc.GetTextExtent(strLine).cx); } } CRect rect(0, 0, max(0,nLineWidth), 0); dc.DrawText(m_csText, rect, DT_CALCRECT | m_dwTextStyle); dc.SelectObject(pOldFont); rect.bottom += m_rectMargin.top + m_rectMargin.bottom; rect.right += m_rectMargin.left + m_rectMargin.right + 2; if(m_pBitmap) { int nWidth = CBitmapHelper::GetCBitmapWidth(*m_pBitmap); int nHeight = CBitmapHelper::GetCBitmapHeight(*m_pBitmap); rect.bottom += nHeight; if((rect.left + nWidth) > rect.right) rect.right = rect.left + nWidth; } return rect; } CString CToolTipEx::GetFieldFromString(CString ref, int nIndex, TCHAR ch) { CString strReturn; LPCTSTR pstrStart = ref.LockBuffer(); LPCTSTR pstrBuffer = pstrStart; int nCurrent = 0; int nStart = 0; int nEnd = 0; int nOldStart = 0; while (nCurrent <= nIndex && *pstrBuffer != _T('\0')) { if (*pstrBuffer == ch) { nOldStart = nStart; nStart = nEnd+1; nCurrent++; } nEnd++; pstrBuffer++; } // May have reached the end of the string if (*pstrBuffer == _T('\0')) { nOldStart = nStart; nEnd++; } ref.UnlockBuffer(); if (nCurrent < nIndex) { //TRACE1("Warning: GetStringField - Couldn't find field %d.\n", nIndex); return strReturn; } return ref.Mid(nOldStart, nEnd-nOldStart-1); } LPLOGFONT CToolTipEx::GetSystemToolTipFont() { static LOGFONT LogFont; NONCLIENTMETRICS ncm; ncm.cbSize = sizeof(NONCLIENTMETRICS); if (!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0)) return FALSE; memcpy(&LogFont, &(ncm.lfStatusFont), sizeof(LOGFONT)); return &LogFont; } BOOL CToolTipEx::SetLogFont(LPLOGFONT lpLogFont, BOOL bRedraw /*=TRUE*/) { ASSERT(lpLogFont); if (!lpLogFont) return FALSE; LOGFONT LogFont; // Store font as the global default memcpy(&LogFont, lpLogFont, sizeof(LOGFONT)); // Create the actual font object m_Font.DeleteObject(); m_Font.CreateFontIndirect(&LogFont); if (bRedraw && ::IsWindow(GetSafeHwnd())) Invalidate(); return TRUE; } void CToolTipEx::SetBitmap(CBitmap *pBitmap) { DELETE_BITMAP m_pBitmap = pBitmap; } void CToolTipEx::OnSize(UINT nType, int cx, int cy) { CWnd::OnSize(nType, cx, cy); if(::IsWindow(m_RichEdit.GetSafeHwnd()) == FALSE) return; CRect cr; GetClientRect(cr); // cr.DeflateRect(0, 0, 15, 0); m_RichEdit.MoveWindow(cr); } BOOL CToolTipEx::IsCursorInToolTip() { CRect cr; GetWindowRect(cr); CPoint cursorPos; GetCursorPos(&cursorPos); return cr.PtInRect(cursorPos); } void CToolTipEx::SetRTFText(const CString &csRTF) { m_RichEdit.SetRTF(csRTF); m_csRTF = csRTF; } void CToolTipEx::SetToolTipText(const CString &csText) { m_RichEdit.SetText(csText); m_csText = csText; m_RichEdit.SetFont(&m_Font); } UINT CToolTipEx::OnNcHitTest(CPoint point) { CRect crWindow; GetWindowRect(crWindow); const static int nBorder = 10; if((point.y < crWindow.top + nBorder) && (point.x < crWindow.left + nBorder)) return HTTOPLEFT; else if((point.y < crWindow.top + nBorder) && (point.x > crWindow.right - nBorder)) return HTTOPRIGHT; else if((point.y > crWindow.bottom - nBorder) && (point.x > crWindow.right - nBorder)) return HTBOTTOMRIGHT; else if((point.y > crWindow.bottom - nBorder) && (point.x < crWindow.left + nBorder)) return HTBOTTOMLEFT; if(point.y < crWindow.top + nBorder) return HTTOP; else if(point.y > crWindow.bottom - nBorder) return HTBOTTOM; else if(point.x > crWindow.right - nBorder) return HTRIGHT; else if(point.x < crWindow.left + nBorder) return HTLEFT; // if(point.x > crWindow.right - 15) // return HTCAPTION; return CWnd::OnNcHitTest(point); } void CToolTipEx::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) { CWnd::OnActivate(nState, pWndOther, bMinimized); if (nState == WA_INACTIVE) { Hide(); if(m_pNotifyWnd) { m_pNotifyWnd->PostMessage(NM_INACTIVE_TOOLTIPWND, 0, 0); } } }