FreeScan/OXToolTipCtrl.cpp

1060 lines
29 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ==========================================================================
// Class Implementation : COXToolTipCtrl
// ==========================================================================
// Source file : OXToolTipCtrl.cpp
// Copyright © Dundas Software Ltd. 1999, All Rights Reserved
// //////////////////////////////////////////////////////////////////////////
#include "OXToolTipCtrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// TODO: Add message handlers (eg TTM_SETTIPBKCOLOR) and point them
// to the correct functions.
LPCTSTR COXToolTipCtrl::m_szArrowSpace = _T(" ");
/////////////////////////////////////////////////////////////////////////////
// COXToolTipCtrl construction
COXToolTipCtrl::COXToolTipCtrl() :
m_pParentWnd(NULL),
m_rectMargin(2,2,3,3),
m_nMaxWidth(0),
m_ptOffset(0,20),
m_pCurrentToolTip(NULL),
m_nCheckInterval(500), // Time between checks of the tooltip - allows
// user to move the mouse to the tooltip before
// it disappears
m_nDisplayDelay(500), // delay in milliseconds till the tooltip is
// displayed
m_nDisplayTime(5000), // Length of time the tooltip is displayed
m_nElapsedTime(0),
m_bActivated(TRUE),
m_bTipCancelled(FALSE),
m_bHasExtendedText(FALSE),
m_hOldFocusWnd(NULL),
m_crBackColor(CLR_DEFAULT),
m_crTextColor(CLR_DEFAULT),
m_bUsingSystemFont(TRUE),
m_dwTextStyle(DT_EXPANDTABS|DT_EXTERNALLEADING|DT_NOPREFIX|DT_WORDBREAK)
{
m_arrTools.RemoveAll();
}
COXToolTipCtrl::~COXToolTipCtrl()
{
m_Font.DeleteObject();
COXToolTipInfo *pInfo = NULL;
int nSize = m_arrTools.GetSize();
for (int nIndex = 0; nIndex < nSize; nIndex++)
{
pInfo = (COXToolTipInfo* )m_arrTools.GetAt(nIndex);
delete pInfo;
}
m_arrTools.RemoveAll();
if (IsWindow(m_hWnd))
DestroyWindow();
}
/////////////////////////////////////////////////////////////////////////////
// COXToolTipCtrl message handlers
BEGIN_MESSAGE_MAP(COXToolTipCtrl, CWnd)
//{{AFX_MSG_MAP(COXToolTipCtrl)
ON_WM_PAINT()
ON_WM_TIMER()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_SETFOCUS()
ON_WM_DESTROY()
ON_WM_SETTINGCHANGE()
ON_WM_MOUSEACTIVATE()
//}}AFX_MSG_MAP
ON_MESSAGE(WM_SETFONT, OnSetFont)
ON_MESSAGE(WM_GETFONT, OnGetFont)
END_MESSAGE_MAP()
// --- In  :
// --- Out :
// --- Returns :
// --- Effect : Draws the tooltip - called in response to a WM_PAINT message
void COXToolTipCtrl::OnPaint()
{
if (!m_pCurrentToolTip)
return;
CString str = GetTooltipText(m_pCurrentToolTip);
if (str.IsEmpty())
return;
CPaintDC dc(this); // device context for painting
CRect rect;
GetClientRect(rect);
CBrush Brush, *pOldBrush;
if (m_pCurrentToolTip->clrBackColor == CLR_DEFAULT)
Brush.CreateSolidBrush(GetSysColor(COLOR_INFOBK));
else
Brush.CreateSolidBrush(m_pCurrentToolTip->clrBackColor);
pOldBrush = dc.SelectObject(&Brush);
if (m_pCurrentToolTip->clrTextColor == CLR_DEFAULT)
dc.SetTextColor(GetSysColor(COLOR_INFOTEXT));
else
dc.SetTextColor(m_pCurrentToolTip->clrTextColor);
CFont *pOldFont = dc.SelectObject(&m_Font);
// Draw Border
dc.FillRect(&rect, &Brush);
dc.SelectStockObject(NULL_BRUSH);
dc.SelectStockObject(BLACK_PEN);
dc.Rectangle(rect);
// Draw Text
dc.SetBkMode(TRANSPARENT);
rect.DeflateRect(m_rectMargin);
dc.DrawText(str, rect, m_dwTextStyle);
if (m_bHasExtendedText)
{
TEXTMETRIC tm;
dc.GetTextMetrics(&tm);
int nYMargin = max(tm.tmExternalLeading + tm.tmInternalLeading, tm.tmDescent);
int nXMargin = tm.tmAveCharWidth / 2;
CSize size = dc.GetTextExtent(m_szArrowSpace);
if ((size.cy & 1))
size.cy--;
CPoint pt[4];
if (m_bExtended)
{
pt[0] = CPoint(rect.left + size.cx - nXMargin, rect.top + nYMargin);
pt[1] = CPoint(rect.left, rect.top + size.cy/2);
pt[2] = CPoint(rect.left + size.cx - nXMargin, rect.top + size.cy - nYMargin);
pt[3] = pt[0];
}
else
{
pt[0] = CPoint(rect.right - size.cx + nXMargin, rect.top + nYMargin);
pt[1] = CPoint(rect.right, rect.top + size.cy/2);
pt[2] = CPoint(rect.right - size.cx + nXMargin, rect.top + size.cy - nYMargin);
pt[3] = pt[0];
}
dc.SelectStockObject(BLACK_BRUSH);
dc.Polygon(pt, 4);
}
// Cleanup
dc.SelectObject(pOldBrush);
dc.SelectObject(pOldFont);
}
// --- In  : nIDEvent - The timer event
// --- Out :
// --- Returns :
// --- Effect : Timer events are used to either Activate the tooltip after
// the mouse has been stationary for the specified time, Remove
// the tip after a specified display time, or peridodically check
// that the mouse is still within the bounds of the tool
void COXToolTipCtrl::OnTimer(UINT nIDEvent)
{
CPoint pt;
COXToolTipInfo *pToolTip = NULL;
if (!m_pCurrentToolTip || m_bTipCancelled)
{
KillTimer(nIDEvent);
Pop();
return;
}
switch (nIDEvent)
{
// The mouse has been still for sufficient time. If it's still in the
// initial tool, then show the tooltip for that tool.
case eIDDisplayToolEvent:
KillTimer(eIDDisplayToolEvent);
if (IsCursorInTool(m_pCurrentToolTip))
{
GetCursorPos(&pt);
pt += m_ptOffset;
DisplayToolTip(pt);
m_nElapsedTime = 0;
SetTimer(eIDCheckToolEvent, m_nCheckInterval, NULL);
}
break;
// The tooltip is visible, check it's (a) not been around too long, and
// (b) still in the bounding rect (or tooltip window). If all is well then
// reset the egg timer and check again later.
case eIDCheckToolEvent:
m_nElapsedTime += m_nCheckInterval;
GetCursorPos(&pt);
m_pParentWnd->ScreenToClient(&pt);
pToolTip = FindToolFromPoint(pt);
// Check that the cursor isn't over a new tool
if (pToolTip && pToolTip != m_pCurrentToolTip)
{
KillTimer(eIDCheckToolEvent);
Pop();
return;
}
if (!IsCursorInTool(m_pCurrentToolTip) || (m_nElapsedTime >= m_nDisplayTime))
{
if (IsCursorInToolTip())
{
//TRACE0("Cursor in tooltip - will check later\n");
//SetTimer(eIDCheckToolEvent, m_nCheckInterval, NULL);
}
else
{
//TRACE0("Not in tool or tooltip anymore, or expired. Destroying\n");
KillTimer(eIDCheckToolEvent);
Pop();
//m_pCurrentToolTip = NULL;
}
}
else
{
//TRACE0("Everything's OK - will check later\n");
//SetTimer(eIDCheckToolEvent, m_nCheckInterval, NULL);
}
break;
default:
ASSERT(FALSE);
}
}
// --- In  : nFlags - unused
// point - unused
// --- Out :
// --- Returns :
// --- Effect : Causes a switch between standard and extended info viewing
void COXToolTipCtrl::OnLButtonDown(UINT /*nFlags*/, CPoint /*point*/)
{
// Sometimes a click comes through the pipeline after the tool
// has already been removed
if (!m_pCurrentToolTip || m_bTipCancelled)
return;
if (m_bHasExtendedText)
{
CRect rect;
GetWindowRect(rect);
DisplayToolTip(rect.TopLeft(), !m_bExtended);
}
if (m_hOldFocusWnd)
::SetFocus(m_hOldFocusWnd);
//CWnd::OnLButtonDown(nFlags, point);
}
// --- In  : pOldWnd - Contains the CWnd object that loses the input focus
// (may be NULL).
// --- Out :
// --- Returns :
// --- Effect : Restores the focus back to the previous window (the tooltip does
// not need the focus at all)
void COXToolTipCtrl::OnSetFocus(CWnd* pOldWnd)
{
CWnd::OnSetFocus(pOldWnd);
m_hOldFocusWnd = pOldWnd->GetSafeHwnd();
}
int COXToolTipCtrl::OnMouseActivate(CWnd* /*pDesktopWnd*/, UINT /*nHitTest*/,
UINT /*message*/)
{
return MA_NOACTIVATE;
}
// --- In  :
// --- Out :
// --- Returns :
// --- Effect : Kills off any remaining timers
void COXToolTipCtrl::OnDestroy()
{
KillTimer(eIDDisplayToolEvent);
KillTimer(eIDCheckToolEvent);
CWnd::OnDestroy();
}
// --- In  : wFlag - system-wide parameter flag (see WM_SETTINGCHANGE)
// lpszSection - name of changed section or registry has has changes
// --- Out :
// --- Returns :
// --- Effect : If the tooltip font has not been overriden, this updates the
// font with the new system tooltip font
void COXToolTipCtrl::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
{
CWnd::OnSettingChange(uFlags, lpszSection);
if (m_bUsingSystemFont)
SetLogFont(GetSystemToolTipFont(), TRUE);
}
// --- In  : hFont - Specifies a handle to the new font
// --- Out :
// --- Returns : No return value
// --- Effect : sets the new tooltip font
LRESULT COXToolTipCtrl::OnSetFont(WPARAM hFont, LPARAM /*lParam */)
{
LRESULT result = Default();
// Get the logical font from the supplied handle
LOGFONT lf;
if (!GetObject((HFONT) hFont, sizeof(LOGFONT), &lf))
{
SetLogFont(GetSystemToolTipFont());
return result;
}
SetLogFont(&lf, TRUE);
return result;
}
// --- In  : The parameters are not used
// --- Out :
// --- Returns : The return value is a handle to the font used by the control
// --- Effect :
LRESULT COXToolTipCtrl::OnGetFont(WPARAM /*wParam*/, LPARAM /*lParam*/)
{
return (LRESULT) (HFONT) m_Font;
}
/////////////////////////////////////////////////////////////////////////////
// COXToolTipCtrl operations
BOOL COXToolTipCtrl::Create(CWnd* pParentWnd)
{
m_pParentWnd = 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,
0, 0, 10, 10, // size & position updated when needed
pParentWnd->GetSafeHwnd(), 0, NULL))
{
return FALSE;
}
SetLogFont(GetSystemToolTipFont(), FALSE);
return TRUE;
}
void COXToolTipCtrl::RelayEvent(MSG* pMsg)
{
ASSERT(m_pParentWnd);
if (pMsg->message == WM_MOUSEMOVE)
{
CPoint pt = pMsg->pt;
m_pParentWnd->ScreenToClient(&pt);
// Get the tool under the cursor
COXToolTipInfo *pToolTip = FindToolFromPoint(pt);
// If the tip has been cancelled and the mouse has moved into a "no tool"
// area, then we can reset the current tool in order for a new tool to be
// set as soon as it goes back into tool territory.
if (!pToolTip && m_bTipCancelled)
m_pCurrentToolTip = NULL;
// If no tooltip (we are in a no tooltip fly zone), but we have a potential
// tooltip, then just relax and let the timer do it's thing.
if (!pToolTip || (m_pCurrentToolTip && (pToolTip == m_pCurrentToolTip)))
return;
// First check the if we have a current tooltip, that it's
// still in the current tool or tooltip (if not, dismiss it)
if (m_pCurrentToolTip != NULL)
{
// Cursor over tooltip?
if (IsCursorInToolTip())
return;
if (pToolTip && (pToolTip != m_pCurrentToolTip))
{
StartNewTool(pToolTip);
return;
}
// Cursor over tool?
//if (!IsCursorInTool(m_pCurrentToolTip))
// return; // Allow the timer to deal with this instead of killing now
}
else // m_pCurrentToolTip == NULL
{
// If we have a tool under the cursor then prepare to display
if (pToolTip)
{
StartNewTool(pToolTip);
return;
}
}
}
else if (pMsg->message == WM_LBUTTONDOWN)
{
if (!IsCursorInToolTip())
Pop();
}
else if ((pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST) ||
//(pMsg->message >= WM_SYSKEYFIRST && pMsg->message <= WM_SYSKEYLAST) ||
(/*pMsg->message == WM_LBUTTONDOWN ||*/ pMsg->message == WM_LBUTTONDBLCLK) ||
(pMsg->message == WM_RBUTTONDOWN || pMsg->message == WM_RBUTTONDBLCLK) ||
(pMsg->message == WM_MBUTTONDOWN || pMsg->message == WM_MBUTTONDBLCLK) ||
(pMsg->message == WM_NCLBUTTONDOWN || pMsg->message == WM_NCLBUTTONDBLCLK) ||
(pMsg->message == WM_NCRBUTTONDOWN || pMsg->message == WM_NCRBUTTONDBLCLK) ||
(pMsg->message == WM_NCMBUTTONDOWN || pMsg->message == WM_NCMBUTTONDBLCLK))
{
// The user has interupted the current tool - dismiss it
Pop();
}
}
BOOL COXToolTipCtrl::AddTool(CWnd* pWnd, UINT nIDText, LPCRECT lpRectTool /*=NULL*/,
UINT nIDTool /*=0*/)
{
CString str;
str.LoadString(nIDText);
return AddTool(pWnd, (LPCTSTR) str, lpRectTool, nIDTool);
}
BOOL COXToolTipCtrl::AddTool(CWnd* pWnd, LPCTSTR lpszText,
LPCRECT lpRectTool /*=NULL*/,
UINT nIDTool /*=0*/)
{
ASSERT(pWnd);
if (!pWnd)
return FALSE;
COXToolTipInfo *pToolTip = new COXToolTipInfo;
ASSERT( pToolTip != NULL );
if (pToolTip == NULL)
return FALSE;
// Fill the tooltip structure
pToolTip->hWnd = pWnd->GetSafeHwnd();
pToolTip->nIDTool = nIDTool;
if (lpszText == LPSTR_TEXTCALLBACK)
{
pToolTip->szText = lpszText;
pToolTip->strText.Empty();
}
else
{
pToolTip->szText = NULL;
pToolTip->strText = lpszText;
}
pToolTip->clrBackColor = m_crBackColor;
pToolTip->clrTextColor = m_crTextColor;
pToolTip->nWidth = m_nMaxWidth;
pToolTip->lParam = 0;
// Get bounding region for tooltip info
CRect rect;
if (lpRectTool)
{
rect = lpRectTool;
pWnd->ClientToScreen(rect);
}
else
{
pWnd->GetWindowRect(rect);
}
m_pParentWnd->ScreenToClient(rect);
pToolTip->rectBounds = rect;
// add to the list
m_arrTools.Add(pToolTip);
return TRUE;
}
void COXToolTipCtrl::DelTool(CWnd* pWnd, UINT nIDTool /*=0*/)
{
int nSize = m_arrTools.GetSize();
for (int i = 0; i < nSize; i++)
{
COXToolTipInfo* pToolInfo = (COXToolTipInfo*) m_arrTools.GetAt(i);
if (!pToolInfo)
continue;
if (pWnd->GetSafeHwnd() == pToolInfo->hWnd &&
nIDTool == pToolInfo->nIDTool)
{
if (pToolInfo == m_pCurrentToolTip)
{
Pop();
m_pCurrentToolTip = NULL;
}
delete pToolInfo;
m_arrTools.RemoveAt(i, 1);
}
}
}
void COXToolTipCtrl::GetText(CString& str, CWnd* pWnd, UINT nIDTool /*=0*/)
{
COXToolTipInfo* pToolInfo = GetToolInfoPtr(pWnd, nIDTool);
if (pToolInfo)
str = GetTooltipText(pToolInfo);
}
void COXToolTipCtrl::GetMargin( LPRECT lprc) const
{
ASSERT(lprc);
lprc->top = m_rectMargin.top;
lprc->left = m_rectMargin.left;
lprc->bottom = m_rectMargin.bottom;
lprc->right = m_rectMargin.right;
}
void COXToolTipCtrl::SetDelayTime(DWORD dwDuration, int nTime)
{
switch (dwDuration)
{
case TTDT_AUTOPOP:
m_nDisplayTime = nTime;
break;
case TTDT_INITIAL:
m_nDisplayDelay = nTime;
break;
case TTDT_RESHOW:
default:
ASSERT(FALSE);
}
}
int COXToolTipCtrl::GetDelayTime(DWORD dwDuration) const
{
switch (dwDuration)
{
case TTDT_AUTOPOP:
return m_nDisplayTime;
case TTDT_INITIAL:
return m_nDisplayDelay;
case TTDT_RESHOW:
default:
ASSERT(FALSE);
return 0;
}
}
int COXToolTipCtrl::GetMaxTipWidth() const
{
return m_nMaxWidth;
}
int COXToolTipCtrl::SetMaxTipWidth(int nWidth)
{
int nOldWidth = m_nMaxWidth;
m_nMaxWidth = nWidth;
// Update the width for all tooltips whose width is standard.
// non-standard widths have obviously been modified outside
// of this function, so leave them be.
int nSize = m_arrTools.GetSize();
for (int i = 0; i < nSize; i++)
{
COXToolTipInfo* pTool = (COXToolTipInfo*) m_arrTools.GetAt(i);
if (!pTool)
continue;
if (pTool->nWidth == nOldWidth)
pTool->nWidth = nWidth;
}
return nOldWidth;
}
COLORREF COXToolTipCtrl::GetTipBkColor() const
{
return m_crBackColor;
}
void COXToolTipCtrl::SetTipBkColor(COLORREF clr)
{
// Update the width for all tooltips whose width is standard.
// non-standard widths have obviously been modified outside
// of this function, so leave them be.
int nSize = m_arrTools.GetSize();
for (int i = 0; i < nSize; i++)
{
COXToolTipInfo* pTool = (COXToolTipInfo*) m_arrTools.GetAt(i);
if (!pTool)
continue;
if (pTool->clrBackColor == m_crBackColor)
pTool->clrBackColor = clr;
}
m_crBackColor = clr;
}
COLORREF COXToolTipCtrl::GetTipTextColor() const
{
return m_crTextColor;
}
void COXToolTipCtrl::SetTipTextColor(COLORREF clr)
{
// Update the width for all tooltips whose width is standard.
// non-standard widths have obviously been modified outside
// of this function, so leave them be.
int nSize = m_arrTools.GetSize();
for (int i = 0; i < nSize; i++)
{
COXToolTipInfo* pTool = (COXToolTipInfo*) m_arrTools.GetAt(i);
if (!pTool)
continue;
if (pTool->clrTextColor == m_crTextColor)
pTool->clrTextColor = clr;
}
m_crTextColor = clr;
}
BOOL COXToolTipCtrl::HitTest(CWnd *pWnd, POINT pt, OXTOOLINFO* pToolInfo) const
{
if (!pWnd)
return FALSE;
COXToolTipInfo* pTool = NULL;
// Check that the window has been registered as containing a tool
BOOL bFoundToolWnd = FALSE;
int nSize = m_arrTools.GetSize();
for (int i = 0; i < nSize; i++)
{
pTool = (COXToolTipInfo*) m_arrTools.GetAt(i);
if (!pTool)
continue;
if (pWnd->GetSafeHwnd() == pTool->hWnd ||
::IsChild(pTool->hWnd, pWnd->GetSafeHwnd()))
{
if (pToolInfo)
*pToolInfo = *pTool;
bFoundToolWnd = TRUE;
break;
}
}
// No tool in window - return
if (!bFoundToolWnd || pTool == NULL)
return FALSE;
// The window is a tool - but is the cursor currently over it?
CWnd* pScreenWnd = GetChildWindowFromPoint(pt);
if (pTool->hWnd != pScreenWnd->GetSafeHwnd() &&
!::IsChild(pTool->hWnd, pScreenWnd->GetSafeHwnd()))
{
return FALSE;
}
else
return pTool->rectBounds.PtInRect(pt);
}
void COXToolTipCtrl::Pop()
{
ShowWindow(SW_HIDE);
/* if (::IsWindow(m_hWnd))
ModifyStyle(WS_VISIBLE, 0);
*/
m_bTipCancelled = TRUE;
}
BOOL COXToolTipCtrl::GetToolInfo(OXTOOLINFO& ToolInfo, CWnd* pWnd,
UINT nIDTool /*=0*/)
{
COXToolTipInfo *pToolInfo = GetToolInfoPtr(pWnd, nIDTool);
if (pToolInfo == NULL)
return FALSE;
ToolInfo = *pToolInfo;
return TRUE;
}
void COXToolTipCtrl::SetToolInfo(OXTOOLINFO* pToolInfo)
{
COXToolTipInfo *pInfo = GetToolInfoPtr(CWnd::FromHandle(pToolInfo->hwnd),
pToolInfo->uId);
if (pToolInfo != NULL)
*pInfo = *pToolInfo;
}
/////////////////////////////////////////////////////////////////////////////
// COXToolTipCtrl implementation
CString COXToolTipCtrl::GetTooltipText(COXToolTipInfo *pToolTip)
{
CString strTooltipText(_T(""));
if (!pToolTip)
return strTooltipText;
if (pToolTip->szText == LPSTR_TEXTCALLBACK)
{
NMTTDISPINFO nmtt;
nmtt.hdr.hwndFrom = m_hWnd;
nmtt.hdr.idFrom = ::GetDlgCtrlID(pToolTip->hWnd);
nmtt.hdr.code = TTN_NEEDTEXT;
nmtt.lpszText = NULL;
nmtt.hinst = NULL;
nmtt.uFlags = 0;
m_pParentWnd->SendMessage(WM_NOTIFY, (WPARAM) pToolTip->nIDTool, (LPARAM) &nmtt);
if (nmtt.hinst != NULL)
{
TCHAR sz[512];
UINT nLen = ::LoadString(nmtt.hinst, (UINT) nmtt.lpszText, sz, 512);
ASSERT(nLen < 511);
if (nLen > 0)
strTooltipText = sz;
else
strTooltipText.Empty();
}
else if (nmtt.lpszText)
strTooltipText = nmtt.lpszText;
else
strTooltipText = nmtt.szText;
}
else
strTooltipText = pToolTip->strText;
m_bHasExtendedText = (strTooltipText.Find(_T('\r')) != -1);
if (m_bHasExtendedText)
{
if (m_bExtended)
strTooltipText = m_szArrowSpace + GetFieldFromString(strTooltipText, 1, _T('\r'));
else
strTooltipText = GetFieldFromString(strTooltipText, 0, _T('\r')) + m_szArrowSpace;
}
return strTooltipText;
}
LPLOGFONT COXToolTipCtrl::GetSystemToolTipFont() const
{
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 COXToolTipCtrl::SetLogFont(LPLOGFONT lpLogFont, BOOL bRedraw /*=TRUE*/)
{
ASSERT(lpLogFont);
if (!lpLogFont)
return FALSE;
// Store font as the global default
memcpy(&m_LogFont, lpLogFont, sizeof(LOGFONT));
// If the new font is not the same as the system font, then we will no
// longer update the font in the event of a System Settings Change.
LPLOGFONT lpSysFont = GetSystemToolTipFont();
// Not reliable
//m_bUsingSystemFont = (memcmp(&m_LogFont, lpSysFont, sizeof(LOGFONT)) == 0);
m_bUsingSystemFont = (m_LogFont.lfWeight == lpSysFont->lfWeight &&
m_LogFont.lfItalic == lpSysFont->lfItalic &&
m_LogFont.lfHeight == lpSysFont->lfHeight &&
m_LogFont.lfCharSet == lpSysFont->lfCharSet &&
!_tcscmp(m_LogFont.lfFaceName, lpSysFont->lfFaceName));
// Create the actual font object
m_Font.DeleteObject();
m_Font.CreateFontIndirect(&m_LogFont);
if (bRedraw && ::IsWindow(GetSafeHwnd()))
Invalidate();
return TRUE;
}
CRect COXToolTipCtrl::GetBoundsRect(CString strText, int nWidth) const
{
CWindowDC dc(NULL);
CFont *pOldFont = (CFont*) dc.SelectObject((CFont*)&m_Font);
int nLineWidth = nWidth;
if (nLineWidth == 0)
{
// Count the number of lines of text
int nStart = 0, nNumLines = 0;
CString strTextCopy=strText;
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(strText, i, _T('\n')) + _T(" ");
nLineWidth = max(nLineWidth, dc.GetTextExtent(strLine).cx);
}
}
CRect rect(0,0, max(0,nLineWidth), 0);
dc.DrawText(strText, rect, DT_CALCRECT | m_dwTextStyle);
rect.bottom += m_rectMargin.top + m_rectMargin.bottom;
rect.right += m_rectMargin.left + m_rectMargin.right;
dc.SelectObject(pOldFont);
return rect;
}
CRect COXToolTipCtrl::CalculateInfoBoxRect(CPoint& pt, COXToolTipInfo* pToolTip,
CRect& rectTextBounds) const
{
CRect InfoRect = rectTextBounds;
InfoRect.OffsetRect(-InfoRect.TopLeft());
InfoRect.OffsetRect(pt);
// Need to check it'll fit on screen
CSize ScreenSize(::GetSystemMetrics(SM_CXSCREEN),
::GetSystemMetrics(SM_CYSCREEN));
// Too far right?
if (InfoRect.right > ScreenSize.cx)
InfoRect.OffsetRect(-(InfoRect.right - ScreenSize.cx), 0);
// Too far left?
if (InfoRect.left < 0)
InfoRect.OffsetRect( -InfoRect.left, 0);
// Bottom falling out of screen?
if (InfoRect.bottom > ScreenSize.cy)
{
CRect ParentRect;
::GetWindowRect(pToolTip->hWnd, ParentRect);
int nHeight = InfoRect.Height();
InfoRect.top = ParentRect.top - nHeight;
InfoRect.bottom = InfoRect.top + nHeight;
}
return InfoRect;
}
void COXToolTipCtrl::StartNewTool(COXToolTipInfo* pToolInfo)
{
KillTimer(eIDDisplayToolEvent);
KillTimer(eIDCheckToolEvent);
Pop();
m_bTipCancelled = FALSE;
m_pCurrentToolTip = pToolInfo;
SetTimer(eIDDisplayToolEvent, m_nDisplayDelay, NULL);
}
BOOL COXToolTipCtrl::IsCursorInTool(COXToolTipInfo* pToolInfo) const
{
ASSERT(m_pParentWnd);
CPoint pt;
GetCursorPos(&pt);
m_pParentWnd->ScreenToClient(&pt);
return HitTest(CWnd::FromHandle(pToolInfo->hWnd), pt, NULL);
}
BOOL COXToolTipCtrl::IsCursorInToolTip() const
{
ASSERT(m_pParentWnd);
// Is tooltip visible?
if (m_bTipCancelled || !IsVisible() || !IsWindow(m_hWnd))
return FALSE;
CPoint pt;
GetCursorPos(&pt);
CRect rect;
GetWindowRect(rect);
return rect.PtInRect(pt);
}
COXToolTipInfo* COXToolTipCtrl::FindToolFromPoint(POINT& pt)
{
ASSERT(m_pParentWnd);
CWnd* pWnd = GetChildWindowFromPoint(pt);
if (!pWnd ||
pWnd->GetSafeHwnd() == m_pParentWnd->GetSafeHwnd())
return NULL;
OXTOOLINFO ti;
BOOL bHitTest = HitTest(pWnd, pt, &ti);
if (bHitTest)
return GetToolInfoPtr(CWnd::FromHandle(ti.hwnd), ti.uId);
else
return NULL;
}
COXToolTipInfo* COXToolTipCtrl::GetToolInfoPtr(CWnd* pWnd, UINT nIDTool /*=0*/)
{
int nSize = m_arrTools.GetSize();
for (int i = 0; i < nSize; i++)
{
COXToolTipInfo* pToolInfo = (COXToolTipInfo*) m_arrTools.GetAt(i);
if (!pToolInfo)
continue;
if (pWnd->GetSafeHwnd() == pToolInfo->hWnd &&
nIDTool == pToolInfo->nIDTool)
{
return pToolInfo;
}
}
return NULL;
}
CWnd* COXToolTipCtrl::GetChildWindowFromPoint(POINT& point) const
{
ASSERT(m_pParentWnd);
CPoint pt = point;
// Find the window under the cursor
m_pParentWnd->ClientToScreen(&pt);
HWND hWnd = ::WindowFromPoint(pt);
// WindowFromPoint misses disabled windows and such - go for a more
// comprehensive search in this case.
if (hWnd == m_pParentWnd->GetSafeHwnd())
hWnd = m_pParentWnd->ChildWindowFromPoint(point, CWP_ALL)->GetSafeHwnd();
// Check that we aren't over the parent or out of client area
if (!hWnd || hWnd == m_pParentWnd->GetSafeHwnd())
return NULL;
// if it's not part of the main parent window heirachy, then we are
// not interested
if (!::IsChild(m_pParentWnd->GetSafeHwnd(), hWnd))
return NULL;
return CWnd::FromHandle(hWnd);
}
void COXToolTipCtrl::DisplayToolTip(CPoint& pt, BOOL bExtended /*= FALSE*/)
{
ASSERT(::IsWindow(m_hWnd));
if (!m_bActivated || !m_pCurrentToolTip)
return;
m_bExtended = bExtended;
CString str = GetTooltipText(m_pCurrentToolTip);
if (str.IsEmpty())
return;
//calculate the width and height of the box dynamically
CRect rect = GetBoundsRect(str, m_pCurrentToolTip->nWidth);
rect = CalculateInfoBoxRect(pt, m_pCurrentToolTip, rect);
ShowWindow(SW_HIDE);
SetWindowPos(NULL,
rect.left, rect.top,
rect.Width(), rect.Height(),
SWP_SHOWWINDOW|SWP_NOCOPYBITS|SWP_NOACTIVATE|SWP_NOZORDER);
// ModifyStyle(0, WS_VISIBLE);
// ShowWindow(SW_SHOWNA);
}
CString COXToolTipCtrl::GetFieldFromString(CString ref, int nIndex, TCHAR ch) const
{
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);
}