출처 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=50&MAEULNo=20&no=587123&ref=587080
9. 툴팁과 타이틀팁(Tooltip & Titletip)
9.1 헤더를 위한 툴팁
헤더컨트롤을 위한 툴팁을 추가하는것은 매우 간단합니다.
CListCtrl 파생 클래스에 CToolTipCtrl 타입의 멤버변수를 선언합니다.
CToolTipCtrl m_tooltip;
CListCtrl 파생 클래스에서 PreSubclassWindow() 함수를 오버라이드 합니다. 베이스클래스의 PreSubclassWindow()를 호출한후, 툴팁객체를 만듭니다. OnCreate()대신에 PreSubclassWindow() 를 오버라이드한 이유는 컨트롤은 보통 다이얼로그 리소스로부터 생성됨으로써 이미 만들어진후에 C++ 객체에 붙기(Attach) 때문에, OnCreate가 객체에 대해 전혀 호출이 되지 않기때문입니다.
void CMyListCtrl::PreSubclassWindow()
{
CListCtrl::PreSubclassWindow();
// Add initialization code
m_tooltip.Create( this );
m_tooltip.AddTool( GetDlgItem(0), “Right click for context menu” );
}
PreTranslateMessage()함수를 오버라이드해서 CToolTip 객체의 RelayEvents()함수를 호출합니다.
BOOL CMyListCtrl::PreTranslateMessage(MSG* pMsg)
{
m_tooltip.RelayEvent( pMsg );
return CListCtrl::PreTranslateMessage(pMsg);
}
9.2 각각의 컬럼 헤더를 위한 툴팁
컬럼헤더에 툴팁을 제공하는것은 여러 용도가 있습니다. 헤더툴팁이 정말 유용하다고 느낀 한가지 경우는 컬럼의 넓이가 제한되어 있을때 입니다. 툴팁은 컬럼헤더가 제한된 넓이때문에 전달하지 못하는 것을 전달할 수 있습니다. 코드를 Modular하게 하기 위해 우리는 CListCtrl 파생클래스에 툴팁기능을 구현할것입니다.
CListCtrl 파생 클래스에 CToolTipCtrl 타입의 멤버변수를 선언합니다.
CToolTipCtrl m_tooltip;
CListCtrl 파생 클래스에서 PreSubclassWindow() 함수를 오버라이드 합니다. 베이스클래스의 PreSubclassWindow()를 호출한후, 툴팁객체를 만듭니다.
void CMyListCtrl::PreSubclassWindow()
{
CListCtrl::PreSubclassWindow();
// Add initialization code
m_tooltip.Create( this );
}
PreTranslateMessage()함수를 오버라이드해서 CToolTip 객체의 RelayEvents()함수를 호출합니다. RelayEvents() 함수를 호출하는것은 툴팁이 마우스가 툴영역 어디에 들어왔는지는 알 수 있는 기회를 제공합니다. 비록 리스트뷰컨트롤이 받는 모든 메시지를 패스하지만, 툴팁컨트롤은 WM_?BUTTONDOWN,WM_?BUTTONUP, 그리고 WM_MOUSEMOVE 메시지 만을 처리합니다.
BOOL CMyListCtrl::PreTranslateMessage(MSG* pMsg)
{
m_tooltip.RelayEvent( pMsg );
return CListCtrl::PreTranslateMessage(pMsg);
}
툴팁을 추가하기위한 방법을 제공합니다. 하나의 툴팁컨트롤은 여러개의 툴을 처리할수 있습니다. AddHeaderToolTip() 헬퍼 함수는 툴팁컨트롤에 새로운 툴을 추가합니다
// AddHeaderToolTip – 컬럼헤더에 대한 툴팁을 추가합니다.
// 컨트롤은 자세히보기(LVS_REPORT) 모드여야 합니다.
// Returns – 성공시 TRUE 리턴
// nCol – 컬럼 인덱스
// sTip – 툴팁텍스트
BOOL CMyListCtrl::AddHeaderToolTip(int nCol, LPCTSTR sTip /*= NULL*/)
{
const int TOOLTIP_LENGTH = 80;
char buf[TOOLTIP_LENGTH+1];
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
int nColumnCount = pHeader->GetItemCount();
if( nCol >= nColumnCount) return FALSE;
if( (GetStyle() & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
// 헤더의 높이를 구합니다.
RECT rect;
pHeader->GetClientRect( &rect );
int height = rect.bottom;
RECT rctooltip;
rctooltip.top = 0;
rctooltip.bottom = rect.bottom;
// 컬럼의 좌우 테두리를 구합니다.
rctooltip.left = 0 – GetScrollPos( SB_HORZ );
for( int i = 0; i < nCol; i++ ) rctooltip.left += GetColumnWidth( i );
rctooltip.right = rctooltip.left + GetColumnWidth( nCol );
if( sTip == NULL ) {
// 컬럼 헤딩 문자열을 가져옵니다.
LV_COLUMN lvcolumn;
lvcolumn.mask = LVCF_TEXT;
lvcolumn.pszText = buf;
lvcolumn.cchTextMax = TOOLTIP_LENGTH;
if( !GetColumn( nCol, &lvcolumn ) ) return FALSE;
}
m_tooltip.AddTool( GetDlgItem(0), sTip ? sTip : buf, &rctooltip, nCol+1 ); return TRUE;
}
OnNotify()를 오버라이드하여 컬럼 넓이에 대한 변동사항을 추적합니다.만약 사용자가 컬럼크기를 조정했을때 툴팁정보를 갱신하지 않는다면 툴팁은 정확한 컬럼을 보여주지 않을것입니다.
BOOL CMyListCtrl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
HD_NOTIFY *pHDN = (HD_NOTIFY*) lParam;
if((pHDN->hdr.code == HDN_ENDTRACKA || pHDN->hdr.code == HDN_ENDTRACKW)) {
// 툴팁의 정보를 갱신합니다.
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
int nColumnCount = pHeader->GetItemCount();
CToolInfo toolinfo;
toolinfo.cbSize = sizeof( toolinfo );
// 영향을 받은 각 컬럼을 tooltipinfo 를 통해 순환(Cycle)합니니다.
for( int i = pHDN->iItem; i <= nColumnCount; i++ ) {
m_tooltip.GetToolInfo( toolinfo, pHeader, i + 1 );
int dx; // 넓이의 변경사항을 저장합니다.
if( i == pHDN->iItem )
dx = pHDN->pitem->cxy – toolinfo.rect.right;
else
toolinfo.rect.left += dx;
toolinfo.rect.right += dx;
m_tooltip.SetToolInfo( &toolinfo );
}
}
return CListCtrl::OnNotify(wParam, lParam, pResult);
}
9.3 각 셀을 위한 툴팁
툴팁은 컬럼넓이가 제한된 화면 사이즈때문에 제한될때 매우 유용합니다. 이들은 단축되서 보여지는 컬럼의 문자열들을 확장하는데 사용될 수도 있습니다. 이 일을위해 MFC 에 의해 제공되는 툴팁기능을 사용할것입니다. 아래코드는 셀의 문자열을 툴팁에 보여줍니다만, 이것은 쉽게 이미 셀에 보여주고 있는것을 보이기보다 좀 다른것을 보여주는 것으로 수정될 수 있습니다.
각 셀에 대해 툴팁을 추가하는 것은 상당히 쉽습니다. 어쨌든 문서는 별로 도움이되지 못하며, 인식해야할 몇가지 사항이 있습니다. NT 4.0 과 Win95 의 리스트뷰 컨트롤은 몇가지 중요한 차이점을 가지고 있습니다. 첫째로 , 리스트 뷰 컨트롤과툴팁 컨트롤은 Windows 95 상에서는 ANSI 컨트롤입니다. 이것이 의미하는 것은 Windows 95 상에서는 메시지들이 ANSI 버젼이라는 것입니다. 프로젝트 세팅에 따라서 메시지 상수들(A 나 W 접미어가 없는것)이 맞는값으로 변환되는것에 의존하지마십시오. 예를 들어 UNICODE 프로그램을 개발한다면 TTN_NEEDTEXT는 TTN_NEEDTEXTW로 번역되어야 합니다. 하지만 Windows 95 에서는 실제 메시지는 TTN_NEEDTEXTA 입니다. 이것은 또한 구조체와 문자열에도 적용됩니다. Windows 95 에서는 컨트롤에전달되는 모든 문자열은 ANSI 문자열이어야 합니다. NT 4.0에선 컨트롤들이 모두UNICODE 컨트롤입니다.
둘째로, NT 4.0 에서는 리스트뷰 컨트롤이 자동으로 툴팁컨트롤을 만듭니다. 이 내장 툴팁 컨트롤은 마우스가 리스트뷰 컨트롤위에 올라와 잠시 움직이지 않을 때 자동적으로 TTN_NEEDTEXTW 통지를 보내게 됩니다. 아래코드는 이 내장 툴팁 컨트롤로부터의 통지를 무시합니다.
PreSubclassWindow()를 오버라이드하고 베이스 클래스의 PreSubclassWindow()를 호출한후 EnableToolTips(TRUE)를 호출합니다. EnableToolTips()함수는 CWnd 클래스의멤버 함수이고, 따라서 모든 윈도우와 컨트롤에 사용이 가능합니다.
void CMyListCtrl::PreSubclassWindow()
{
CListCtrl::PreSubclassWindow();
// Add initialization code
EnableToolTips(TRUE);
}
OnToolHitTest()함수를 오버라이드 합니다. OnToolHitTest()는 CWnd클래스에 정의된 가상함수이고 프레임워크에 의해서 마우스포인터가 어떤툴위에 있는지 결정하기위해호출됩니다. 툴은 컨트롤 윈도우이거나 또는 윈도우안의 사각형 영역일수도 있습니다우리의 목적을 위해선 각 셀의 영역이 툴로 처리되어야 합니다.
OnToolHitTest() 문서(Documentation)는 툴을 찾았다면 1을 못찾았다면 -1을 리턴하라고 암시합니다.하지만 실제로 프레임워크는 리턴값을 툴이 바뀌었는지 결정하는데 사용합니다. 프레임워크는 툴이 바뀌었을때만 툴팁을 갱신하므로, OnToolHitTest()는 지정된 점의 셀이 값(문자열)이 바뀌어었을때 다른 값을 리턴해야 합니다.
int CMyListCtrl::OnToolHitTest(CPoint point, TOOLINFO * pTI) const
{
int row, col;
RECT cellrect;
row = CellRectFromPoint(point, &cellrect, &col );
if ( row == -1 ) return -1;
pTI->hwnd = m_hWnd;
pTI->uId = (UINT)((row<<10)+(col&0x3ff)+1);
pTI->lpszText = LPSTR_TEXTCALLBACK;
pTI->rect = cellrect;
return pTI->uId;
}
이 함수는 처음에 CellRectFromPoint() 함수를 호출하여 로우와 컬럼, 그리고 셀의 경계 사각형을 알아냅니다.
CellRectFromPoint() 는 아래에서 살펴봅니다. 함수는그리고 TOOLINFO 구조체를 설정합니다. ‘uId’ 는 줄,열의 값을 결합한 값을 지정합니다. 로우와 컬럼의 결합방법은 4194303 개의 로우와 1023개의 컬럼을 허용합니다. 또한 결과에 1이 더해진것을 주의하십시오. 이렇게 하는 이유는 0이 아닌값만을 생성하기위해서 입니다. 우리는 NT4.0 에서 자동적으로 만들어진 툴팁에서 보낸 통지와 구별하기 위해서 0이 아닌값을 필요로 합니다. 앞에서 언급했듯이 NT 4.0 에서 만들어진 리스트뷰 컨트롤은 자동적으로 툴팁을 생성하며 이 툴팁의 ID 는 0입니다.
다음 우리는 OnToolHitTest() 에서 사용된 CellRectFromPoint() 함수를 정의합니다. 이 함수는 #1 – 4.4 에서 다루어진 HitTestEx() 함수와 매우 비슷합니다. 점 위치의 로우 와 컬럼값을 알아내는것에 더해서 이 함수는 점아래 셀의 경계 사각형도 알아냅니다.
// CellRectFromPoint – 셀의 로우,컬럼,경계사각형을 알아냅니다.
// Returns – 성공시 로우 인덱스, 아니면 -1
// point – 검사될 점(현재 마우스 포인터)
// cellrect – 경계사각형을 저장할 변수
// col – 컬럼값을 저장할 변수
int CMyListCtrl::CellRectFromPoint(CPoint & point, RECT * cellrect, int * col) const
{
int colnum;
// 리스트뷰가 LVS_REPORT 모드에있는지 확인
if( (GetWindowLong(m_hWnd, GWL_STYLE) & LVS_TYPEMASK) != LVS_REPORT )
return -1;
// 현재 화면에 보이는처음과 끝 Row 를 알아내기
int row = GetTopIndex();
int bottom = row + GetCountPerPage();
if( bottom > GetItemCount() ) bottom = GetItemCount();
// 컬럼갯수 알아내기
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
int nColumnCount = pHeader->GetItemCount();
// 현재보이는 Row 들간에 루프 돌기
for( ;row <=bottom;row++) {
// 아이템의 경계 사각형을 가져오고, 점이 포함되는지 체크
CRect rect;
GetItemRect( row, &rect, LVIR_BOUNDS );
if( rect.PtInRect(point) ) {
// 컬럼찾기
for( colnum = 0; colnum < nColumnCount; colnum++ ) {
int colwidth = GetColumnWidth(colnum);
if( point.x >= rect.left && point.x <= (rect.left + colwidth) ) {
RECT rectClient;
GetClientRect( &rectClient );
if( col ) *col = colnum;
rect.right = rect.left + colwidth;
// 오른쪽 끝이 클라이언트 영역을 벗어나지 않도록 확인
if( rect.right > rectClient.right )
rect.right = rectClient.right;
*cellrect = rect;
return row;
}
rect.left += colwidth;
}
}
}
return -1;
}
OnToolTipText()함수를 정의합니다. 이것은 툴팁으로부터의 TTN_NEEDTEXT통지에 대한 핸들러입니다. 실제로 OnToolTipText()는 TTN_NEEDTEXTA와 TTN_NEEDTEXTW 통지 양쪽을 처리하며 프로그램자신이 ANSI든 UNICODE든 관계없이 전자를 위해 ANSI 스트링을사용하며 후자를 위해 UNICODE 스트링을 사용합니다.
BOOL CMyListCtrl::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
{
// ANSI 와 UNICODE 양쪽버젼의 메시지를 처리해야함
TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
CString strTipText;
UINT nID = pNMHDR->idFrom;
if( nID == 0 ) // NT 에서의 자동생성 툴팁으로부터의 통지
return FALSE; // 그냥 빠져나간다.
int row = ((nID-1) >> 10) & 0x3fffff ;
int col = (nID-1) & 0x3ff;
strTipText = GetItemText( row, col );
#ifndef _UNICODE
if (pNMHDR->code == TTN_NEEDTEXTA)
lstrcpyn(pTTTA->szText, strTipText, 80);
else
_mbstowcsz(pTTTW->szText, strTipText, 80);
#else
if (pNMHDR->code == TTN_NEEDTEXTA)
_wcstombsz(pTTTA->szText, strTipText, 80);
else
lstrcpyn(pTTTW->szText, strTipText, 80);
#endif
*pResult = 0;
return TRUE; // 메시지가 처리되었다.
}
함수는 먼저 NT에서의 내장툴팁 통지인지 체크하고 맞다면 바로 리턴합니다. 그리고 id로부터 로우와 컬럼정보를 해독하고 셀안의 정보로 TOOLTIPTEXT구조체를 채웁니다.
메시지맵에 OnToolTipText()를 연결하십시오. ON_NOTIFY_EX 와 ON_NOTIFY_EX_RANGE매크로를 사용하는 것이 좋습니다. 이것은 필요하다면 더이상의 메시지 처리를 위해 통지를 전달하는 것을 가능하게 합니다.
BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl)
//{{AFX_MSG_MAP(CMyListCtrl)
:
// other entries
:
//}}AFX_MSG_MAP
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
END_MESSAGE_MAP()
우리가 단순한 ON_NOTIFY 매크로를 사용하지 않는다는것에 주의하십시오. 실제로 당신이 ON_NOTIFY(TTN_NEEDTEXT, 0, OnToolTipText) 같은 메시지맵 엔트리를 사용한다면 몇개의 큰 문제점을 가지게 됩니다. 첫째로 TTN_NEEDTEXT 통지는 ANSI 빌드버젼에선 NT 4.0 상에서는 절대로 받을수 없는 TTN_NEEDTEXTA 로 변환됩니다. 둘째로, 우리는 일반적인 경우가 아닌 ID 0 이외의 값에 관심이 있다는 것입니다.
9.4 각 셀을 위한 타이틀팁
타이틀은 다소 툴팁과 비슷합니다. 리스트뷰 컨트롤에 대해서, 타이틀팁은 텍스트를 다 보여줄수 있을만큼 넓지 못할 때 사용됩니다. 타이틀팁은 마우스가 셀의 위에 위치하지마자 보여지게 됩니다.
우리는 타이틀팁을 위해 커스텀클래스를 정의합니다. 타이틀팁은 WM_MOUSEMOVE 핸들러에서 생성됩니다. 타이틀 팁은 마우스가 아이템 밖으로 나가거나 응용프로그램이 포커스를 잃을때 자기 자신을 파괴하게 됩니다.
OnMouseMove() 코드는 CellRectFromPoint() 함수를 로우,컬럼인덱스와 하부아이템의 경계영역 사각형을 알아내기 위해 사용합니다. 그리고 타이틀팁 객체에 사각형과 아이템 텍스트의 정보를 넘기게 됩니다. 타이틀팁객체가 화면에 보여질지를 결정하게됩니다.
void CMyListCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
if( nFlags == 0 ) { // 타이틀팁 객체 Enable 하기 위해서
int row, col;
RECT cellrect;
row = CellRectFromPoint(point, &cellrect, &col );
if( row != -1 ) {
int offset = 5; // 컬럼 왼쪽 경계에서부터의 오프셋은 보통 5픽셀
// 첫번째 컬럼에 대해선 오프셋은 그림을 고려해야 합니다.
// 아래오프셋은 자기자신의 프로그램에 맞는값을 사용하십시오
if( col == 0 ) offset+=19;
m_titletip.Show( cellrect, GetItemText( row, col ), offset );
}
}
CListCtrl::OnMouseMove(nFlags, point);
}
PreSubclassWindow() 함수가 오버라이드 되어있지 않다면, 해야합니다. 이 함수에서타이틀팁 객체를 생성할것입니다.
void CMyListCtrl::PreSubclassWindow()
{
CListCtrl::PreSubclassWindow();
// Add initialization code
m_titletip.Create( this );
}
헤더파일과 구현파일이 아래 있습니다.
#if !defined( AFX_TITLETIP_H__FB05F243_E98F_11D0_82A3_20933B000000__INCLUDED_ )
#define AFX_TITLETIP_H__FB05F243_E98F_11D0_82A3_20933B000000__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
// TitleTip.h : header file
// CTitleTip window
#define TITLETIP_CLASSNAME _T(“ZTitleTip”)
class CTitleTip : public CWnd
{
public: // Construction
CTitleTip();
public: // Attributes
public: // Operations
//Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CTitleTip)
public:
virtual BOOL PreTranslateMessage(MSG* pMsg);
//}}AFX_VIRTUAL
public: // Implementation
void Show( CRect rectTitle, LPCTSTR lpszTitleText, int xoffset = 0);
virtual BOOL Create( CWnd *pParentWnd);
virtual ~CTitleTip();
protected:
CWnd *m_pParentWnd;
CRect m_rectTitle;
protected: // Generated message map functions
//{{AFX_MSG(CTitleTip)
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately// before the previous line.
#endif // !defined(AFX_TITLETIP_H__FB05F243_E98F_11D0_82A3_20933B000000__INCLUDED_)
CTitleTip 의 생성자에서 이 프로그램의 다른 인스턴스에서 윈도우의 클래스를 등록하지 않았다면 등록하게 됩니다. 클래스에 대한 배경 브러쉬는 COLOR_INFOBK를사용하게 되어 툴팁의 색상과 같게 됩니다.
Create() 함수에서는 주목해서 볼것이 윈도우 스타입니다. WS_BORDER 스타일이 타이틀팁 윈도우 주위에 경계선을 그리게 합니다. WS_POPUP스타일은 타이틀팁이 리스트뷰 컨트롤의 경계를 벗어날수도 있게 하기위해 필요합니다. WS_EX_TOOLWINDOW스타일은 윈도우가 태스크바에 나타나지 않도록 해줍니다. WS_EX_TOPMOST 스타일은 타이틀팁이 보이도록 해줍니다.
Show() 함수는 텍스트의 크기가 셀의 크기보다 클때 타이틀팁을 보여주게 됩니다. 경계 사각형은 변형되고 나중에 언제 타이틀팁이 숨겨져야 할때를 결정하기 위해 저장됩니다.
WM_MOUSEMOVE 에 대한 핸들러 OnMouseMove() 는 타이틀팁이 보일 셀영역에 마우스가있는지를 검사합니다. 이 영역은 타이틀팁 윈도우의 클라이언트 영역 사각형보다 작습니다. 만약 마우스가 영역밖으로 나간다면 타이틀이 숨겨지고 적절한 윈도우에 WM_MOUSEMOVE 메시지가 전달됩니다.
타이틀팁은 또한 사용자가 키나 마우스버튼을 눌렀을때 없어질 필요가 있습니다. 우리는 이메시지들에 대해 살펴보기 위해 PreTranslateMessage()를 오버라이드 합니다. 만약 이 메시지들중에 어떤것이라도 받는다면 타이틀팁은 없어지고 리스트뷰 컨트롤에 메시지가 전달됩니다.
// TitleTip.cpp : implementation file
#include “stdafx.h”
#include “TitleTip.h”
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// CTitleTip
CTitleTip::~CTitleTip() { }
CTitleTip::CTitleTip()
{
// 만약 이미 등록되지 않았다면 윈도우 클래스를 등록한다.
WNDCLASS wndcls;
HINSTANCE hInst = AfxGetInstanceHandle();
if(!(::GetClassInfo(hInst, TITLETIP_CLASSNAME, &wndcls))) {
// 새로운 클래스 등록
wndcls.style = CS_SAVEBITS ;
wndcls.lpfnWndProc = ::DefWindowProc;
wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
wndcls.hInstance = hInst;
wndcls.hIcon = NULL;
wndcls.hCursor = LoadCursor( hInst, IDC_ARROW );
wndcls.hbrBackground = (HBRUSH)(COLOR_INFOBK + 1);
wndcls.lpszMenuName = NULL;
wndcls.lpszClassName = TITLETIP_CLASSNAME;
if (!AfxRegisterClass(&wndcls)) AfxThrowResourceException();
}
}
BEGIN_MESSAGE_MAP(CTitleTip, CWnd)
//{{AFX_MSG_MAP(CTitleTip)
ON_WM_MOUSEMOVE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// CTitleTip message handlers
BOOL CTitleTip::Create(CWnd * pParentWnd)
{
ASSERT_VALID(pParentWnd);
DWORD dwStyle = WS_BORDER | WS_POPUP;
DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
m_pParentWnd = pParentWnd;
return CreateEx( dwExStyle, TITLETIP_CLASSNAME, NULL, dwStyle, 0, 0, 0, 0, NULL, NULL, NULL );
}
void CTitleTip::Show(CRect rectTitle,LPCTSTR lpszTitleText,int xoffset /*=0*/)
{
ASSERT( ::IsWindow( m_hWnd ) );
ASSERT( !rectTitle.IsRectEmpty() );
if( IsWindowVisible() ) return;
m_rectTitle.top = -1;
m_rectTitle.left = -xoffset;
m_rectTitle.right = rectTitle.Width()-xoffset;
m_rectTitle.bottom = rectTitle.Height();
m_pParentWnd->ClientToScreen( rectTitle );
CClientDC dc(this);
CString strTitle(lpszTitleText);
CFont *pFont = m_pParentWnd->GetFont();
CFont *pFontDC = dc.SelectObject( pFont );
CRect rectDisplay = rectTitle;
CSizesize = dc.GetTextExtent( strTitle );
rectDisplay.left += xoffset;
rectDisplay.right = rectDisplay.left + size.cx + 5;
// 만약 텍스트가 공간에 맞다면 보이지 않는다.
if( rectDisplay.right <= rectTitle.right-xoffset ) return;
SetWindowPos( &wndTop, rectDisplay.left, rectDisplay.top,
rectDisplay.Width(), rectDisplay.Height(),
SWP_SHOWWINDOW|SWP_NOACTIVATE );
dc.SetBkMode( TRANSPARENT );
dc.TextOut( 0, -1, strTitle );
dc.SelectObject( pFontDC );
SetCapture();
}
void CTitleTip::OnMouseMove(UINT nFlags, CPoint point)
{
if( !m_rectTitle.PtInRect( point ) ) {
ReleaseCapture();
ShowWindow( SW_HIDE );
// 메시지를 전달한다.
ClientToScreen( &point );
CWnd *pWnd = WindowFromPoint( point );
if( pWnd == this ) pWnd = m_pParentWnd;
pWnd->ScreenToClient( &point );
pWnd->PostMessage(WM_MOUSEMOVE, nFlags, MAKELONG( point.x, point.y ));
}
}
BOOL CTitleTip::PreTranslateMessage(MSG* pMsg)
{
CWnd *pWnd;
switch( pMsg->message ) {
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
POINTS pts = MAKEPOINTS( pMsg->lParam );
POINT point;
point.x = pts.x;
point.y = pts.y;
ClientToScreen( &point );
pWnd = WindowFromPoint( point );
if( pWnd == this ) pWnd = m_pParentWnd;
pWnd->ScreenToClient( &point );
pMsg->lParam = MAKELONG( point.x, point.y );
// 그냥 밑으로 가게 합니다.
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
ReleaseCapture( );
ShowWindow( SW_HIDE );
m_pParentWnd->PostMessage(pMsg->message, pMsg->wParam, pMsg->lParam);
return TRUE;
}
if( GetFocus() == NULL ) {
ReleaseCapture();
ShowWindow( SW_HIDE );
return TRUE;
}
return CWnd::PreTranslateMessage(pMsg);
}
– the end of this article –