// HmVerticalTab.cpp: implementation of the CHmVerticalTab class.
|
//
|
//////////////////////////////////////////////////////////////////////
|
|
#include "stdafx.h"
|
#include "HmVerticalTab.h"
|
|
#ifdef _DEBUG
|
#undef THIS_FILE
|
static char THIS_FILE[]=__FILE__;
|
#define new DEBUG_NEW
|
#endif
|
|
//////////////////////////////////////////////////////////////////////
|
// Construction/Destruction
|
//////////////////////////////////////////////////////////////////////
|
|
CHmVerticalTab::CHmVerticalTab()
|
{
|
m_hWnd = NULL;
|
m_crBkgnd = RGB(250, 250, 255);
|
m_nPaddingTop = 12;
|
m_nItemMarginTop = 1;
|
m_crText[0] = RGB(17, 14, 39);
|
m_crText[1] = RGB(255, 255, 255);
|
m_crItemBkgnd[0] = RGB(67, 153, 233);
|
m_crItemBkgnd[1] = RGB(188, 219, 248);
|
m_hbrItemBkgnd[0] = CreateSolidBrush(m_crItemBkgnd[0]);
|
m_hbrItemBkgnd[1] = CreateSolidBrush(m_crItemBkgnd[1]);
|
m_pPressItem = nullptr;
|
m_pHighItem = nullptr;
|
|
LOGBRUSH lb;
|
lb.lbColor = RGB(225, 127, 39);
|
lb.lbHatch = 0;
|
lb.lbStyle = BS_SOLID;
|
DWORD iStyle = PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_SQUARE | PS_JOIN_MITER;
|
m_hPenUnder[0] = ExtCreatePen(iStyle, 3, &lb, 0, NULL);
|
lb.lbColor = RGB(225, 127, 39);
|
m_hPenUnder[1] = ExtCreatePen(iStyle, 2, &lb, 0, NULL);
|
lb.lbColor = RGB(223, 226, 230);
|
m_hPenUnderWnd = ExtCreatePen(iStyle, 1, &lb, 0, NULL);
|
m_nCurSel = 0;
|
m_nItemHeight = 36;
|
m_bShowHightLine = FALSE;
|
}
|
|
CHmVerticalTab::~CHmVerticalTab()
|
{
|
if (m_hPenUnder[0] != nullptr) {
|
::DeleteObject(m_hPenUnder[0]);
|
}
|
if (m_hPenUnder[1] != nullptr) {
|
::DeleteObject(m_hPenUnder[1]);
|
}
|
|
if (m_hPenUnderWnd != nullptr) {
|
::DeleteObject(m_hPenUnderWnd);
|
}
|
}
|
|
BOOL CHmVerticalTab::RegisterWndClass()
|
{
|
WNDCLASS wc;
|
wc.lpszClassName = HMVERTICALTAB_CLASS;
|
wc.hInstance = AfxGetInstanceHandle();
|
wc.lpfnWndProc = WindowProc;
|
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
|
wc.hIcon = 0;
|
wc.lpszMenuName = NULL;
|
wc.hbrBackground = NULL;
|
wc.style = CS_GLOBALCLASS|CS_DBLCLKS;
|
wc.cbClsExtra = 0;
|
wc.cbWndExtra = 0;
|
|
// ×¢²á×Ô¶¨ÒåÀà
|
return (::RegisterClass(&wc) != 0);
|
}
|
|
CHmVerticalTab* CHmVerticalTab::Hook(HWND hWnd)
|
{
|
CHmVerticalTab* pHmTab = (CHmVerticalTab*)GetProp(hWnd, TAG_HMVERTICALTAB);
|
if(pHmTab == NULL)
|
{
|
pHmTab = new CHmVerticalTab();
|
pHmTab->m_hWnd = hWnd;
|
|
SetProp(hWnd, TAG_HMVERTICALTAB, (HANDLE)pHmTab);
|
}
|
|
return pHmTab;
|
}
|
|
void CHmVerticalTab::Release()
|
{
|
::DeleteObject(m_hbrItemBkgnd[0]);
|
::DeleteObject(m_hbrItemBkgnd[1]);
|
|
// delete
|
delete this;
|
}
|
|
void CHmVerticalTab::SetPaddingTop(int value)
|
{
|
m_nPaddingTop = value;
|
}
|
|
void CHmVerticalTab::SetItemMarginTop(int value)
|
{
|
m_nItemMarginTop = value;
|
}
|
|
void CHmVerticalTab::SetTextColor(COLORREF color1, COLORREF color2)
|
{
|
m_crText[0] = color1;
|
m_crText[1] = color2;
|
InvalidateRect(m_hWnd, NULL, TRUE);
|
}
|
|
void CHmVerticalTab::SetItemBkgndColor(COLORREF color1, COLORREF color2)
|
{
|
m_crItemBkgnd[0] = color1;
|
m_crItemBkgnd[1] = color2;
|
::DeleteObject(m_hbrItemBkgnd[0]);
|
::DeleteObject(m_hbrItemBkgnd[1]);
|
m_hbrItemBkgnd[0] = CreateSolidBrush(m_crItemBkgnd[0]);
|
m_hbrItemBkgnd[1] = CreateSolidBrush(m_crItemBkgnd[1]);
|
|
InvalidateRect(m_hWnd, NULL, TRUE);
|
}
|
|
void CHmVerticalTab::SetHightLineVisible(BOOL bVisible)
|
{
|
m_bShowHightLine = bVisible;
|
}
|
|
int CHmVerticalTab::AddItem(const char* pszText, void* pContext/* = nullptr*/, BOOL bUpdate/* = TRUE*/)
|
{
|
HMVERTICALTABITEM item;
|
memset(&item, 0, sizeof(HMVERTICALTABITEM));
|
strcpy_s(item.szText, HMVERTICALTAB_ITEM_TEXT_MAX, pszText);
|
item.context = pContext;
|
m_items.push_back(item);
|
|
if (bUpdate) {
|
InvalidateRect(m_hWnd, NULL, TRUE);
|
}
|
|
return 0;
|
}
|
|
int CHmVerticalTab::DeleteItem(const char* pszText, BOOL bUpdate/* = TRUE*/)
|
{
|
for (auto iter = m_items.begin(); iter != m_items.end(); iter++) {
|
if (strcmp((*iter).szText, pszText) == 0) {
|
m_items.erase(iter);
|
break;
|
}
|
}
|
|
if (bUpdate) {
|
InvalidateRect(m_hWnd, NULL, TRUE);
|
}
|
|
return 0;
|
}
|
|
void* CHmVerticalTab::GetContext(unsigned int index)
|
{
|
if (index >= m_items.size()) return nullptr;
|
auto& item = m_items.at(index);
|
return item.context;
|
}
|
|
void CHmVerticalTab::Notify(int nCode, DWORD_PTR dwData, DWORD_PTR dwData1/* = 0*/, DWORD_PTR dwData2/* = 0*/)
|
{
|
HWND hParent;
|
hParent = GetParent(m_hWnd);
|
if (hParent != NULL) {
|
HMVERTICALTAB_NMHDR tab_nmhdr;
|
tab_nmhdr.nmhdr.hwndFrom = m_hWnd;
|
tab_nmhdr.nmhdr.idFrom = GetWindowLong(m_hWnd, GWL_ID);
|
tab_nmhdr.nmhdr.code = nCode;
|
tab_nmhdr.dwData = dwData;
|
tab_nmhdr.dwData1 = dwData1;
|
tab_nmhdr.dwData2 = dwData2;
|
SendMessage(hParent, WM_NOTIFY, (WPARAM)tab_nmhdr.nmhdr.idFrom, (LPARAM)&tab_nmhdr);
|
}
|
}
|
|
////////////////////////////////
|
// À¹½Ø´°¿ÚÏûÏ¢º¯Êý
|
LRESULT CALLBACK CHmVerticalTab::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
{
|
CHmVerticalTab* pHmTab = (CHmVerticalTab *)GetProp(hWnd, TAG_HMVERTICALTAB);
|
if(pHmTab == NULL && uMsg != WM_NCCREATE)
|
{
|
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
|
}
|
|
|
// Èç¹ûHookÔòÏìÓ¦ÏûÏ¢
|
ASSERT(hWnd);
|
switch(uMsg)
|
{
|
case WM_NCCREATE:
|
return OnNcCreate(hWnd, wParam, lParam);
|
|
case WM_DESTROY:
|
return pHmTab->OnDestroy(wParam, lParam);
|
|
case WM_PAINT:
|
return pHmTab->OnPaint(wParam, lParam);
|
|
case WM_TIMER:
|
return pHmTab->OnTimer(wParam, lParam);
|
|
case WM_MOUSEMOVE:
|
return pHmTab->OnMouseMove(wParam, lParam);
|
|
case WM_LBUTTONDOWN:
|
return pHmTab->OnLButtonDown(wParam, lParam);
|
|
case WM_SETCURSOR:
|
return pHmTab->OnSetCursor(wParam, lParam);
|
|
case WM_SIZE:
|
return pHmTab->OnSize(wParam, lParam);
|
|
case WM_GETDLGCODE:
|
return DLGC_WANTALLKEYS;
|
|
default:
|
break;
|
}
|
|
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
|
}
|
|
///////////////////////////////
|
// WM_NCCREATE
|
// ´°¿Ú´´½¨Ç°µÄ³õʼ»¯¹¤×÷
|
LRESULT CHmVerticalTab::OnNcCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
|
{
|
CHmVerticalTab* pHmTab = (CHmVerticalTab *)GetProp(hWnd, TAG_HMVERTICALTAB);
|
ASSERT(pHmTab == NULL);
|
|
Hook(hWnd);
|
return ::DefWindowProc(hWnd, WM_NCCREATE, wParam, lParam);
|
}
|
|
///////////////////////////////
|
// WM_DESTROY
|
LRESULT CHmVerticalTab::OnDestroy(WPARAM wParam, LPARAM lParam)
|
{
|
Release();
|
return ::DefWindowProc(m_hWnd, WM_DESTROY, wParam, lParam);
|
}
|
|
///////////////////////////////
|
// WM_TIMER
|
LRESULT CHmVerticalTab::OnTimer(WPARAM wParam, LPARAM lParam)
|
{
|
if (wParam == 1) {
|
POINT pt;
|
GetCursorPos(&pt);
|
::ScreenToClient(m_hWnd, &pt);
|
|
HMVERTICALTABITEM* pLastHighItem = m_pHighItem;
|
HighTest(pt, m_pHighItem, nullptr);
|
if (m_pHighItem != pLastHighItem) {
|
::InvalidateRect(m_hWnd, NULL, TRUE);
|
}
|
|
if (m_pHighItem == nullptr) {
|
::KillTimer(m_hWnd, 1);
|
}
|
}
|
|
return ::DefWindowProc(m_hWnd, WM_TIMER, wParam, lParam);
|
}
|
|
///////////////////////////////
|
// WM_MOUSEMOVE
|
LRESULT CHmVerticalTab::OnMouseMove(WPARAM wParam, LPARAM lParam)
|
{
|
POINT pt;
|
pt.x = LOWORD(lParam);
|
pt.y = HIWORD(lParam);
|
|
HMVERTICALTABITEM* pLastHighItem = m_pHighItem;
|
int nHitCode = HighTest(pt, m_pHighItem, nullptr);
|
if (m_pHighItem != pLastHighItem) {
|
RECT rcClient;
|
GetClientRect(m_hWnd, &rcClient);
|
::InvalidateRect(m_hWnd, &rcClient, TRUE);
|
}
|
if (m_pHighItem != NULL) {
|
::KillTimer(m_hWnd, 1);
|
::SetTimer(m_hWnd, 1, 100, NULL);
|
}
|
|
::SetProp(m_hWnd, HMTAB_HITCODETEST, (HANDLE)(__int64)nHitCode);
|
return ::DefWindowProc(m_hWnd, WM_MOUSEMOVE, wParam, lParam);
|
}
|
|
/*
|
* WM_LBUTTONDOWN
|
* Êó±ê×ó¼ü°´ÏÂ
|
*/
|
LRESULT CHmVerticalTab::OnLButtonDown(WPARAM wParam, LPARAM lParam)
|
{
|
POINT pt, ptNew;
|
pt.x = LOWORD(lParam);
|
pt.y = HIWORD(lParam);
|
|
BOOL bButtonUp = FALSE;
|
int nClickIndex = -1;
|
HighTest(pt, m_pPressItem, &nClickIndex);
|
if (m_pPressItem != NULL) {
|
m_pHighItem = NULL;
|
::InvalidateRect(m_hWnd, NULL, TRUE);
|
}
|
|
|
// ²¶×½Êó±êÏûÏ¢£¬¼ì²âÊÇ·ñÍ϶¯
|
HMVERTICALTABITEM* pPressItem = NULL;
|
if (m_pPressItem != NULL) {
|
::KillTimer(m_hWnd, 1);
|
if (::GetCapture() == NULL) {
|
SetCapture(m_hWnd);
|
ASSERT(m_hWnd == GetCapture());
|
AfxLockTempMaps();
|
for (;;)
|
{
|
MSG msg;
|
VERIFY(::GetMessage(&msg, NULL, 0, 0));
|
|
if (GetCapture() != m_hWnd) break;
|
|
switch (msg.message)
|
{
|
case WM_MOUSEMOVE:
|
ptNew = msg.pt;
|
::ScreenToClient(m_hWnd, &ptNew);
|
HighTest(ptNew, pPressItem, &nClickIndex);
|
if (pPressItem != m_pPressItem) {
|
m_pPressItem = nullptr;
|
::InvalidateRect(m_hWnd, NULL, TRUE);
|
}
|
break;
|
|
case WM_LBUTTONUP:
|
ptNew = msg.pt;
|
::ScreenToClient(m_hWnd, &ptNew);
|
HighTest(ptNew, pPressItem, &nClickIndex);
|
if (m_pPressItem != nullptr && pPressItem == m_pPressItem) {
|
m_nCurSel = nClickIndex;
|
bButtonUp = TRUE;
|
}
|
goto ExitLoop;
|
|
case WM_KEYDOWN:
|
if (msg.wParam == VK_ESCAPE) {
|
goto ExitLoop;
|
}
|
break;
|
|
default:
|
DispatchMessage(&msg);
|
break;
|
}
|
}
|
|
ExitLoop:
|
m_pPressItem = NULL;
|
ReleaseCapture();
|
::InvalidateRect(m_hWnd, NULL, TRUE);
|
|
if (bButtonUp) {
|
Notify((int)HMVERTICALTAB_SEL_CHANGED, m_nCurSel);
|
}
|
|
AfxUnlockTempMaps(FALSE);
|
}
|
}
|
|
|
return ::DefWindowProc(m_hWnd, WM_LBUTTONDOWN, wParam, lParam);
|
}
|
|
///////////////////////////////
|
// WM_SETCURSOR
|
LRESULT CHmVerticalTab::OnSetCursor(WPARAM wParam, LPARAM lParam)
|
{
|
int nHitCode = (int)(__int64)GetProp(m_hWnd, HMTAB_HITCODETEST);
|
switch (nHitCode)
|
{
|
case HMTAB_HT_NOWHERE:
|
case HMTAB_HT_ITEM:
|
SetCursor(::LoadCursor(NULL, IDC_ARROW));
|
return TRUE;
|
|
case HMTAB_HT_HIGT_ITEM:
|
SetCursor(::LoadCursor(NULL, IDC_HAND));
|
return TRUE;
|
|
default:
|
break;
|
}
|
|
return ::DefWindowProc(m_hWnd, WM_SETCURSOR, wParam, lParam);
|
}
|
|
/*
|
* WM_SIZE
|
*/
|
LRESULT CHmVerticalTab::OnSize(WPARAM wParam, LPARAM lParam)
|
{
|
::InvalidateRect(m_hWnd, NULL, TRUE);
|
return ::DefWindowProc(m_hWnd, WM_SIZE, wParam, lParam);
|
}
|
|
///////////////////////////////
|
// WM_PAINT
|
LRESULT CHmVerticalTab::OnPaint(WPARAM wParam, LPARAM lParam)
|
{
|
HDC hDC, hMemDC;
|
HBITMAP hBitmap;
|
RECT rcClient;
|
CString strText;
|
HFONT hFont1, hFont2;
|
HBRUSH hBrushBK;
|
|
|
// BeginPaint
|
PAINTSTRUCT ps;
|
hDC = BeginPaint(m_hWnd, &ps);
|
GetClientRect(m_hWnd, &rcClient);
|
|
hMemDC = ::CreateCompatibleDC(hDC);
|
hBitmap = ::CreateCompatibleBitmap(hDC, rcClient.right - rcClient.left,
|
rcClient.bottom - rcClient.top);
|
::SelectObject(hMemDC, hBitmap);
|
::SetBkMode(hMemDC, TRANSPARENT);
|
|
|
HFONT hFontDefault = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
|
CFont* pFont = CFont::FromHandle(hFontDefault);
|
LOGFONT lf;
|
pFont->GetLogFont(&lf);
|
hFont1 = ::CreateFontIndirect(&lf);
|
lf.lfWeight = FW_BOLD;
|
hFont2 = ::CreateFontIndirect(&lf);
|
|
|
// ±³¾°ÑÕÉ«
|
hBrushBK = CreateSolidBrush(m_crBkgnd);
|
::FillRect(hMemDC, &rcClient, hBrushBK);
|
DeleteObject(hBrushBK);
|
|
|
SelectObject(hMemDC, m_hPenUnderWnd);
|
::MoveToEx(hMemDC, rcClient.left, rcClient.bottom - 2, NULL);
|
::LineTo(hMemDC, rcClient.right, rcClient.bottom - 2);
|
|
|
|
// »æÖÆ×ÓÏî
|
SIZE sizeItem;
|
RECT rcItem, rcText;
|
rcItem.left = rcClient.left;
|
rcItem.right = rcClient.right-1;
|
rcItem.top = m_nPaddingTop;
|
int index = 0;
|
for (int i = 0; i < m_items.size(); i++) {
|
auto& item = m_items.at(i);
|
::SelectObject(hMemDC, i == m_nCurSel ? hFont2 : hFont1);
|
::SetTextColor(hMemDC, i == m_nCurSel ? m_crText[1] : m_crText[0]);
|
::GetTextExtentPoint32(hMemDC, item.szText, (int)strlen(item.szText), &sizeItem);
|
rcItem.top += m_nItemMarginTop;
|
rcItem.bottom = rcItem.top + m_nItemHeight;
|
|
if (i == m_nCurSel) {
|
::FillRect(hMemDC, &rcItem, m_hbrItemBkgnd[0]);
|
}
|
else if(&item == m_pHighItem) {
|
::FillRect(hMemDC, &rcItem, m_hbrItemBkgnd[1]);
|
}
|
|
::CopyRect(&rcText, &rcItem);
|
::DrawText(hMemDC, item.szText, (int)strlen(item.szText), &rcText,
|
DT_VCENTER | DT_CENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
|
::CopyRect(&item.rect, &rcItem);
|
|
|
// ÓÒ²à¸ßÁÁ»®Ïß?
|
if (m_bShowHightLine && (i == m_nCurSel || &item == m_pHighItem)) {
|
HPEN hOldPen = (HPEN)::SelectObject(hMemDC, i == m_nCurSel ? m_hPenUnder[0] : m_hPenUnder[1]);
|
::MoveToEx(hMemDC, item.rect.right - 3, item.rect.top, NULL);
|
::LineTo(hMemDC, item.rect.right - 3, item.rect.bottom);
|
::SelectObject(hMemDC, hOldPen);
|
}
|
|
|
index++;
|
rcItem.top = rcItem.bottom;
|
}
|
::DeleteObject(hFont1);
|
::DeleteObject(hFont2);
|
|
|
// ²à±ßÏß
|
HPEN hPen = CreatePen(PS_SOLID, 1, RGB(202, 204, 211));
|
HPEN hOldPen = (HPEN)::SelectObject(hMemDC, hPen);
|
::MoveToEx(hMemDC, rcClient.right - 1, 0, NULL);
|
::LineTo(hMemDC, rcClient.right - 1, rcClient.bottom - 1);
|
::SelectObject(hMemDC, hOldPen);
|
|
|
// EndPaint
|
::BitBlt(hDC, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
|
hMemDC, 0, 0, SRCCOPY);
|
EndPaint(m_hWnd, &ps);
|
::DeleteObject(hBitmap);
|
::DeleteDC(hMemDC);
|
|
|
return 1;
|
}
|
|
void CHmVerticalTab::SetBkgndColor(COLORREF cr)
|
{
|
m_crBkgnd = cr;
|
}
|
|
/*
|
* ¼ì²â×ø±êµãËùÔÚµÄÏî
|
* ·µ»Ø, TYGTLITEM
|
*/
|
int CHmVerticalTab::HighTest(POINT pt, OUT HMVERTICALTABITEM*& pItem, int* pnIndex)
|
{
|
// ¼ì²âÊÇ·ñÔÚij¸ö×ÓÏî
|
int nRet = HMTAB_HT_NOWHERE;
|
pItem = NULL;
|
for (int i = 0; i < m_items.size(); i++) {
|
auto& item = m_items.at(i);
|
if (::PtInRect(&item.rect, pt)) {
|
pItem = &item;
|
nRet = pItem == m_pHighItem ? HMTAB_HT_HIGT_ITEM : HMTAB_HT_ITEM;
|
if (pnIndex != nullptr) *pnIndex = i;
|
break;
|
}
|
}
|
|
|
return nRet;
|
}
|
|
int CHmVerticalTab::GetCurSel()
|
{
|
return m_nCurSel;
|
}
|
|
void CHmVerticalTab::SetCurSel(int index)
|
{
|
if (0 <= index && index <= m_items.size()) {
|
m_nCurSel = index;
|
InvalidateRect(m_hWnd, NULL, TRUE);
|
}
|
}
|
|
int CHmVerticalTab::GetItemCount()
|
{
|
return (int)m_items.size();
|
}
|