// HmTab.cpp: implementation of the CHmTab class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "HmTab.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CHmTab::CHmTab() { m_hWnd = NULL; m_crBkgnd = RGB(250, 250, 255); m_nPaddingLeft = 28; m_nPaddingBottom = 12; m_nItemMarginLeft = 8; m_crText[0] = RGB(88, 88, 88); m_crText[1] = RGB(18, 18, 18); 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; } CHmTab::~CHmTab() { 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 CHmTab::RegisterWndClass() { WNDCLASS wc; wc.lpszClassName = BYHMTAB_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); } CHmTab* CHmTab::Hook(HWND hWnd) { CHmTab* pHmTab = (CHmTab*)GetProp(hWnd, BYSTAG_HMTAB); if(pHmTab == NULL) { pHmTab = new CHmTab; pHmTab->m_hWnd = hWnd; SetProp(hWnd, BYSTAG_HMTAB, (HANDLE)pHmTab); } return pHmTab; } void CHmTab::Release() { // delete delete this; } void CHmTab::SetPaddingLeft(int value) { m_nPaddingLeft = value; } void CHmTab::SetItemMarginLeft(int value) { m_nItemMarginLeft = value; } void CHmTab::SetTextColor(COLORREF color1, COLORREF color2) { m_crText[0] = color1; m_crText[1] = color2; InvalidateRect(m_hWnd, NULL, TRUE); } int CHmTab::AddItem(const char* pszText,BOOL bUpdate/* = TRUE*/) { HMTABITEM item; memset(&item, 0, sizeof(HMTABITEM)); strcpy_s(item.szText, MHITEM_TEXT_MAX, pszText); m_items.push_back(item); if (bUpdate) { InvalidateRect(m_hWnd, NULL, TRUE); } return 0; } int CHmTab::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 CHmTab::Notify(int nCode, DWORD_PTR dwData, DWORD_PTR dwData1/* = 0*/, DWORD_PTR dwData2/* = 0*/) { HWND hParent; hParent = GetParent(m_hWnd); if (hParent != NULL) { BYHMTAB_NMHDR iii_nmhdr; iii_nmhdr.nmhdr.hwndFrom = m_hWnd; iii_nmhdr.nmhdr.idFrom = GetWindowLong(m_hWnd, GWL_ID); iii_nmhdr.nmhdr.code = nCode; iii_nmhdr.dwData = dwData; iii_nmhdr.dwData1 = dwData1; iii_nmhdr.dwData2 = dwData2; SendMessage(hParent, WM_NOTIFY, (WPARAM)iii_nmhdr.nmhdr.idFrom, (LPARAM)&iii_nmhdr); } } //////////////////////////////// // À¹½Ø´°¿ÚÏûÏ¢º¯Êý LRESULT CALLBACK CHmTab::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CHmTab* pHmTab = (CHmTab *)GetProp(hWnd, BYSTAG_HMTAB); 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 CHmTab::OnNcCreate(HWND hWnd, WPARAM wParam, LPARAM lParam) { CHmTab* pHmTab = (CHmTab *)GetProp(hWnd, BYSTAG_HMTAB); ASSERT(pHmTab == NULL); Hook(hWnd); return ::DefWindowProc(hWnd, WM_NCCREATE, wParam, lParam); } /////////////////////////////// // WM_DESTROY LRESULT CHmTab::OnDestroy(WPARAM wParam, LPARAM lParam) { Release(); return ::DefWindowProc(m_hWnd, WM_DESTROY, wParam, lParam); } /////////////////////////////// // WM_TIMER LRESULT CHmTab::OnTimer(WPARAM wParam, LPARAM lParam) { if (wParam == 1) { POINT pt; GetCursorPos(&pt); ::ScreenToClient(m_hWnd, &pt); HMTABITEM* 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 CHmTab::OnMouseMove(WPARAM wParam, LPARAM lParam) { POINT pt; pt.x = LOWORD(lParam); pt.y = HIWORD(lParam); HMTABITEM* 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 CHmTab::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); } // ²¶×½Êó±êÏûÏ¢£¬¼ì²âÊÇ·ñÍ϶¯ HMTABITEM* 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)BYHMTAB_SEL_CHANGED, m_nCurSel); } AfxUnlockTempMaps(FALSE); } } return ::DefWindowProc(m_hWnd, WM_LBUTTONDOWN, wParam, lParam); } /////////////////////////////// // WM_SETCURSOR LRESULT CHmTab::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 CHmTab::OnSize(WPARAM wParam, LPARAM lParam) { ::InvalidateRect(m_hWnd, NULL, TRUE); return ::DefWindowProc(m_hWnd, WM_SIZE, wParam, lParam); } /////////////////////////////// // WM_PAINT LRESULT CHmTab::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.lfHeight -= 5; 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.top = rcClient.top; rcItem.bottom = rcClient.bottom-1; rcItem.left = m_nPaddingLeft; 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.left += m_nItemMarginLeft; rcItem.right = rcItem.left + (int)sizeItem.cx; ::CopyRect(&rcText, &rcItem); rcText.bottom -= m_nPaddingBottom; ::DrawText(hMemDC, item.szText, (int)strlen(item.szText), &rcText, DT_LEFT | DT_BOTTOM | DT_SINGLELINE | DT_END_ELLIPSIS); ::CopyRect(&item.rect, &rcItem); // Ï»®Ïß? if (i == m_nCurSel || &item == m_pHighItem) { HPEN hOldPen = (HPEN)::SelectObject(hMemDC, i == m_nCurSel ? m_hPenUnder[0] : m_hPenUnder[1]); ::MoveToEx(hMemDC, item.rect.left, item.rect.bottom - 3, NULL); ::LineTo(hMemDC, item.rect.right, item.rect.bottom - 3); ::SelectObject(hMemDC, hOldPen); } index++; rcItem.left = rcItem.right; } ::DeleteObject(hFont1); ::DeleteObject(hFont2); // 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 CHmTab::SetBkgndColor(COLORREF cr) { m_crBkgnd = cr; } /* * ¼ì²â×ø±êµãËùÔÚµÄÏî * ·µ»Ø, TYGTLITEM */ int CHmTab::HighTest(POINT pt, OUT HMTABITEM*& 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 CHmTab::GetCurSel() { return m_nCurSel; } void CHmTab::SetCurSel(int index) { if (0 <= index && index <= m_items.size()) { m_nCurSel = index; InvalidateRect(m_hWnd, NULL, TRUE); } } int CHmTab::GetItemCount() { return (int)m_items.size(); }