557 lines
12 KiB
C++
557 lines
12 KiB
C++
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FILE NAME
|
|
//
|
|
// XInfoTip.cpp
|
|
//
|
|
// COMPONENT
|
|
//
|
|
// CXInfoTip class implementation
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// (see header)
|
|
//
|
|
// AUTHOR
|
|
//
|
|
// Mark Bozeman 09-16-2001
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// This software is released into the public domain.
|
|
// You are free to use it in any way you like.
|
|
//
|
|
// This software is provided "as is" with no expressed
|
|
// or implied warranty. I accept no liability for any
|
|
// damage or loss of business that this software may cause.
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h" // PCH
|
|
#include "XInfoTip.h" // Class interface
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#define CX_ROUNDED 12 // Tip horizontal roundness
|
|
#define CY_ROUNDED 10 // Tip vertical roundness
|
|
#define CX_LEADER 25 // Width of tip lead
|
|
#define CY_LEADER 25 // Height of tip lead
|
|
#define CX_ICON_MARGIN 5 // Width of margin between icon and tip text
|
|
|
|
#define DEFAULT_SHOW_DELAY 500 // Default show delay (ms)
|
|
|
|
#define TIMER_HIDE 500 // Hide timer (ms)
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CXInfoTip::CXInfoTip()
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Constructor
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
CXInfoTip::CXInfoTip()
|
|
{
|
|
// Register the class
|
|
m_szClass = AfxRegisterWndClass(0);
|
|
|
|
m_hIcon = NULL;
|
|
|
|
// Set the delay defaults
|
|
m_nShowDelay = DEFAULT_SHOW_DELAY;
|
|
|
|
m_IconSize = CSize(0, 0);
|
|
m_ptOrigin = CPoint(0, 0);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CXInfoTip::~CXInfoTip()
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Deconstructor
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
CXInfoTip::~CXInfoTip()
|
|
{
|
|
}
|
|
|
|
// Message map
|
|
BEGIN_MESSAGE_MAP(CXInfoTip, CWnd)
|
|
//{{AFX_MSG_MAP(CXInfoTip)
|
|
ON_WM_PAINT()
|
|
ON_WM_CREATE()
|
|
ON_WM_TIMER()
|
|
ON_WM_DESTROY()
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CXInfoTip::Create()
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Creates the tip window
|
|
//
|
|
// RETURNS
|
|
//
|
|
// [BOOL] - TRUE on success, FALSE on failure
|
|
//
|
|
// PARAMETERS
|
|
//
|
|
// [pParentWnd] - Pointer to parent window
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
BOOL CXInfoTip::Create(CWnd* pParentWnd)
|
|
{
|
|
BOOL bSuccess;
|
|
|
|
// Must have a parent
|
|
ASSERT(pParentWnd != NULL);
|
|
|
|
bSuccess = CreateEx(NULL, m_szClass, NULL, WS_POPUP, 0, 0, 0, 0, pParentWnd->GetSafeHwnd(), NULL, NULL);
|
|
|
|
// Use default GUI font for default font
|
|
m_pFont = CFont::FromHandle((HFONT)::GetStockObject(DEFAULT_GUI_FONT));
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CXInfoTip::Show()
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Shows the tip window
|
|
//
|
|
// RETURNS
|
|
//
|
|
// [void]
|
|
//
|
|
// PARAMETERS
|
|
//
|
|
// [szText] - Tip text
|
|
// [pt] - Coordinates to display tip window
|
|
// or NULL to use the current cursor position
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
void CXInfoTip::Show(CString szText, CPoint *pt /* = NULL */)
|
|
{
|
|
if (pt != NULL)
|
|
m_ptOrigin = *pt;
|
|
else
|
|
GetCursorPos(&m_ptOrigin);
|
|
|
|
m_szText = szText;
|
|
|
|
// Start the show timer
|
|
m_nTimer = SetTimer(timerShow, m_nShowDelay, NULL);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CXInfoTip::OnPaint()
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Paint the window
|
|
//
|
|
// RETURNS
|
|
//
|
|
// [void]
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
void CXInfoTip::OnPaint()
|
|
{
|
|
CPaintDC dc( this ); // device context for painting
|
|
|
|
CRect rc;
|
|
CBrush WindowBrush;
|
|
CBrush FrameBrush;
|
|
CBrush InnerFrameBrush;
|
|
HRGN hRegion;
|
|
CRgn *pRegion;
|
|
CFont *pSysFont;
|
|
|
|
// Get the client rectangle
|
|
GetClientRect(rc);
|
|
|
|
// Create the brushes
|
|
InnerFrameBrush.CreateSolidBrush(::GetSysColor(COLOR_SCROLLBAR));
|
|
FrameBrush.CreateSolidBrush(::GetSysColor(COLOR_WINDOWTEXT));
|
|
WindowBrush.CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
|
|
|
|
// Get the window region
|
|
GetWindowRegion(&dc, &hRegion);
|
|
pRegion = CRgn::FromHandle(hRegion);
|
|
|
|
// Draw the frame
|
|
dc.FillRgn(pRegion, &WindowBrush);
|
|
dc.FrameRgn(pRegion, &InnerFrameBrush, 3, 3);
|
|
dc.FrameRgn(pRegion, &FrameBrush, 1, 1);
|
|
|
|
// Adjust the area for the icon
|
|
rc.DeflateRect(CX_ROUNDED, CY_ROUNDED, 0, 0);
|
|
if (m_hIcon != NULL)
|
|
rc.left = rc.left + m_IconSize.cx + CX_ICON_MARGIN;
|
|
|
|
// Set the font
|
|
pSysFont = (CFont *)dc.SelectObject(m_pFont);
|
|
// Draw the tip text
|
|
dc.SetBkMode( TRANSPARENT );
|
|
dc.DrawText(m_szText, &rc, DT_TOP | DT_LEFT);
|
|
|
|
// Draw the icon
|
|
if (m_hIcon != NULL)
|
|
DrawIconEx(dc.m_hDC, CX_ROUNDED, CY_ROUNDED, m_hIcon, m_IconSize.cx, m_IconSize.cy, 0, NULL, DI_NORMAL);
|
|
|
|
// Clean up GDI
|
|
::DeleteObject(hRegion);
|
|
dc.SelectObject(pSysFont);
|
|
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CXInfoTip::GetWindowRegion()
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Retrieves the window region
|
|
//
|
|
// RETURNS
|
|
//
|
|
// [BOOL] - TRUE on success, FALSE on failure
|
|
//
|
|
// PARAMETERS
|
|
//
|
|
// [pDC] - Pointer to display device context
|
|
// [hRegion] - Filled with the calculated window region
|
|
// [Size] - Filled with the calculated window size
|
|
// or NULL.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
BOOL CXInfoTip::GetWindowRegion(CDC* pDC, HRGN* hRegion, CSize *Size /* = NULL */)
|
|
{
|
|
CRect rcWnd;
|
|
POINT ptLeader[3];
|
|
CRgn LeaderRegion;
|
|
CRgn CaptionRegion;
|
|
CFont *pSysFont;
|
|
|
|
ASSERT(pDC != NULL);
|
|
ASSERT(hRegion != NULL);
|
|
|
|
// Calculate the are for the tip text
|
|
pSysFont = (CFont *)pDC->SelectObject(m_pFont);
|
|
pDC->DrawText(m_szText, &rcWnd, DT_CALCRECT);
|
|
pDC->SelectObject(pSysFont);
|
|
|
|
// Adjust for the rounded corners
|
|
rcWnd.InflateRect(CX_ROUNDED, CY_ROUNDED);
|
|
|
|
// Adjust for icon
|
|
if (m_hIcon != NULL)
|
|
rcWnd.right = rcWnd.right + m_IconSize.cx + CX_ICON_MARGIN;
|
|
if (rcWnd.Height() < m_IconSize.cy)
|
|
rcWnd.bottom = rcWnd.top + m_IconSize.cy;
|
|
|
|
// Calculate the leader triangle coordinates
|
|
|
|
ptLeader[0].x = rcWnd.Width() - CX_ROUNDED;
|
|
ptLeader[0].y = rcWnd.Height() - CY_ROUNDED;
|
|
|
|
ptLeader[1].x = ptLeader[0].x;
|
|
ptLeader[1].y = ptLeader[0].y + CY_LEADER;
|
|
|
|
ptLeader[2].x = ptLeader[0].x - CX_LEADER;
|
|
ptLeader[2].y = rcWnd.Height() - CY_ROUNDED;
|
|
|
|
// Create the caption region
|
|
CaptionRegion.CreateRoundRectRgn(0, 0, rcWnd.Width(), rcWnd.Height(), CX_ROUNDED, CY_ROUNDED);
|
|
// Create the leader region
|
|
LeaderRegion.CreatePolygonRgn(ptLeader, 3, ALTERNATE);
|
|
// Create window region
|
|
*hRegion = ::CreateRectRgn(0, 0, rcWnd.Width(), rcWnd.Height() + CY_LEADER);
|
|
// Combine the regions
|
|
CombineRgn(*hRegion, CaptionRegion.operator HRGN(), LeaderRegion.operator HRGN(), RGN_OR);
|
|
|
|
// Set the window size
|
|
if (Size != NULL)
|
|
{
|
|
Size->cx = rcWnd.Width();
|
|
Size->cy = rcWnd.Height() + CY_LEADER;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CXInfoTip::OnCreate()
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Window creation
|
|
//
|
|
// RETURNS
|
|
//
|
|
// [int] - Zero on success, -1 otherwise
|
|
//
|
|
// PARAMETERS
|
|
//
|
|
// [lpCreateStruct] - Pointer to creation structure
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
int CXInfoTip::OnCreate( LPCREATESTRUCT lpCreateStruct )
|
|
{
|
|
if ( CWnd::OnCreate( lpCreateStruct ) == -1 )
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CXInfoTip::OnTimer()
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Timer event
|
|
//
|
|
// RETURNS
|
|
//
|
|
// [void]
|
|
//
|
|
// PARAMETERS
|
|
//
|
|
// [nIDEvent] - Timer identifier
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
void CXInfoTip::OnTimer( UINT nIDEvent )
|
|
{
|
|
HRGN hRegion;
|
|
CSize WindowSize;
|
|
CDC *pDC;
|
|
CPoint ptCursor;
|
|
|
|
switch (nIDEvent)
|
|
{
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Show the tip window
|
|
/////////////////////////////////////////////////////////////////////
|
|
case timerShow:
|
|
KillTimer(m_nTimer);
|
|
|
|
pDC = GetDC();
|
|
GetWindowRegion(pDC, &hRegion, &WindowSize);
|
|
ReleaseDC(pDC);
|
|
|
|
::SetWindowRgn(m_hWnd, hRegion, TRUE);
|
|
|
|
SetWindowPos(&wndTop, m_ptOrigin.x - WindowSize.cx + CX_ROUNDED, m_ptOrigin.y - WindowSize.cy + CY_ROUNDED, WindowSize.cx, WindowSize.cy, SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
|
|
|
m_nTimer = SetTimer(timerHide, TIMER_HIDE, NULL);
|
|
break;
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Hide the tip window
|
|
/////////////////////////////////////////////////////////////////////
|
|
case timerHide:
|
|
GetCursorPos(&ptCursor);
|
|
if (ptCursor != m_ptOrigin)
|
|
{
|
|
KillTimer(m_nTimer);
|
|
ShowWindow(SW_HIDE);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
CWnd::OnTimer(nIDEvent);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CXInfoTip::OnDestroy()
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Window destruction
|
|
//
|
|
// RETURNS
|
|
//
|
|
// [void]
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
void CXInfoTip::OnDestroy()
|
|
{
|
|
KillTimer(m_nTimer);
|
|
|
|
CWnd::OnDestroy();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CXInfoTip::RelayEvent()
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Call this in the parent's PreTranslateMessage() to
|
|
// relay tooltip event messages.
|
|
//
|
|
// RETURNS
|
|
//
|
|
// [void]
|
|
//
|
|
// PARAMETERS
|
|
//
|
|
// [lpMsg] - Pointer to message structure
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
void CXInfoTip::RelayEvent(LPMSG lpMsg)
|
|
{
|
|
CPoint point;
|
|
CWnd *pWindow;
|
|
CString szTooltipText;
|
|
TipToolInfo Info;
|
|
|
|
switch(lpMsg->message)
|
|
{
|
|
case WM_LBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_MBUTTONDOWN:
|
|
ShowWindow(SW_HIDE);
|
|
break;
|
|
case WM_MOUSEMOVE:
|
|
GetCursorPos(&point);
|
|
|
|
if (point != m_ptOrigin)
|
|
{
|
|
// Find the tool
|
|
pWindow = WindowFromPoint(point);
|
|
if (pWindow != NULL)
|
|
{
|
|
if (m_ToolMap.Lookup(pWindow->m_hWnd, Info))
|
|
{
|
|
// Display the tooltip
|
|
m_ptOrigin = point;
|
|
SetIcon(Info.hIcon);
|
|
Show(Info.szText, &point);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Hide the tooltip
|
|
if (point != m_ptOrigin)
|
|
ShowWindow(SW_HIDE);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CXInfoTip::SetIcon()
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Sets the tip window icon
|
|
//
|
|
// RETURNS
|
|
//
|
|
// [void]
|
|
//
|
|
// PARAMETERS
|
|
//
|
|
// [hIcon] - Handle to the icon
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
void CXInfoTip::SetIcon(HICON hIcon)
|
|
{
|
|
ICONINFO IconInfo;
|
|
|
|
m_hIcon = hIcon;
|
|
|
|
if (hIcon == NULL)
|
|
{
|
|
m_IconSize = CSize(0, 0);
|
|
return;
|
|
}
|
|
|
|
// Get the icon sizes
|
|
ZeroMemory(&IconInfo, sizeof(ICONINFO));
|
|
::GetIconInfo(m_hIcon, &IconInfo);
|
|
|
|
m_IconSize.cx = (BYTE)(IconInfo.xHotspot * 2);
|
|
m_IconSize.cy = (BYTE)(IconInfo.yHotspot * 2);
|
|
|
|
::DeleteObject(IconInfo.hbmMask);
|
|
::DeleteObject(IconInfo.hbmColor);
|
|
|
|
if (IsWindow(m_hWnd))
|
|
RedrawWindow();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CXInfoTip::AddTool()
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Adds a tool
|
|
//
|
|
// RETURNS
|
|
//
|
|
// [void]
|
|
//
|
|
// PARAMETERS
|
|
//
|
|
// [pWnd] - Pointer to the tool window
|
|
// [szTooltipText] - Text to display when the cursor hovers
|
|
// over the window (pWnd)
|
|
// [hIcon] - Icon to display in the tooltip, or NULL
|
|
// to display no icon.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
void CXInfoTip::AddTool(CWnd *pWnd, LPCTSTR szTooltipText, HICON hIcon /* = NULL */)
|
|
{
|
|
ASSERT(pWnd != NULL);
|
|
|
|
// Store the tool information
|
|
TipToolInfo Info;
|
|
Info.szText = szTooltipText;
|
|
Info.hIcon = hIcon;
|
|
|
|
// Add the tool
|
|
m_ToolMap.SetAt(pWnd->m_hWnd, Info);
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CXInfoTip::RemoveTool()
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Removes a tool
|
|
//
|
|
// RETURNS
|
|
//
|
|
// [void]
|
|
//
|
|
// PARAMETERS
|
|
//
|
|
// [pWnd] - Pointer to the tool window to remove
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
void CXInfoTip::RemoveTool(CWnd *pWnd)
|
|
{
|
|
m_ToolMap.RemoveKey(pWnd->m_hWnd);
|
|
}
|