/* * This is a part of the BugTrap package. * Copyright (c) 2005-2009 IntelleSoft. * All rights reserved. * * Description: Hyper-link control class. * Author: Maksim Pyatkovskiy. * * This source code is only intended as a supplement to the * BugTrap package reference and related electronic documentation * provided with the product. See these sources for detailed * information regarding the BugTrap package. */ #include "StdAfx.h" #include "HyperLink.h" #include "BugTrapUtils.h" #include "Globals.h" #ifdef _DEBUG #define new DEBUG_NEW #endif const COLORREF CHyperLink::m_rgbBlueColor = RGB(0x00, 0x00, 0xDD); const COLORREF CHyperLink::m_rgbRedColor = RGB(0xDD, 0x00, 0x00); CHyperLink::CHyperLink(void) { m_rgbCurrentColor = m_rgbBlueColor; *m_szLinkURL = _T('\0'); m_hwnd = NULL; m_pfnOldHyperLinkWndProc = NULL; m_nPrevDefButtonID = -1; } /** * @param pszLinkURL - new link URL. */ void CHyperLink::SetLinkURL(PCTSTR pszLinkURL) { if (pszLinkURL) _tcscpy_s(m_szLinkURL, countof(m_szLinkURL), pszLinkURL); else *m_szLinkURL = _T('\0'); } /** * @param point - point coordinates. * @return true if point appears in boundaries of hyper-link text. */ BOOL CHyperLink::HitTest(const POINT& point) const { RECT rect; GetHyperLinkRect(rect); return PtInRect(&rect, point); } /** * @param x - point x-coordinate. * @param y - point y-coordinate. * @return true if point appears in boundaries of hyper-link text. */ BOOL CHyperLink::HitTest(int x, int y) const { POINT point; point.x = x; point.y = y; return HitTest(point); } /** * @param hdc - drawing context. */ void CHyperLink::DrawHyperLink(HDC hdc) const { TCHAR szLinkText[MAX_PATH]; GetWindowText(m_hwnd, szLinkText, countof(szLinkText)); SetBkColor(hdc, GetSysColor(COLOR_BTNFACE)); _ASSERTE(g_pResManager != NULL); HFONT hOldFont = g_pResManager->m_hUnderlinedFont ? SelectFont(hdc, g_pResManager->m_hUnderlinedFont) : NULL; HWND hwndParent = GetParent(m_hwnd); HBRUSH hBrush = hwndParent ? FORWARD_WM_CTLCOLORSTATIC(hwndParent, hdc, m_hwnd, SendMessage) : NULL; if (! hBrush) hBrush = g_pResManager->m_hbrButtonFaceBrush; if (hBrush) { RECT rcClient; GetClientRect(m_hwnd, &rcClient); FillRect(hdc, &rcClient, hBrush); } SetTextColor(hdc, m_rgbCurrentColor); TextOut(hdc, 1, 1, szLinkText, (int)_tcslen(szLinkText)); SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT)); if (hOldFont) SelectFont(hdc, hOldFont); if (GetFocus() == m_hwnd) { RECT rect; GetHyperLinkRect(rect); DrawFocusRect(hdc, &rect); } } void CHyperLink::DoAction(void) const { if (*m_szLinkURL) { _ASSERTE(g_pResManager != NULL); if (g_pResManager->m_hAppStartingCursor) SetCursor(g_pResManager->m_hAppStartingCursor); ShellExecute(NULL, _T("open"), m_szLinkURL, NULL, NULL, SW_SHOWDEFAULT); } else { HWND hwndParent = GetParent(m_hwnd); if (hwndParent) { int nCtrlID = GetDlgCtrlID(m_hwnd); FORWARD_WM_COMMAND(hwndParent, nCtrlID, m_hwnd, BN_CLICKED, PostMessage); } } } /** * @param hwnd - window handle. * @param uMsg - message identifier. * @param wParam - first message parameter. * @param lParam - second message parameter. */ LRESULT CALLBACK CHyperLink::HyperLinkWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { int x, y, nCtrlID; POINT point; HCURSOR hCursor; COLORREF rgbNewColor; PAINTSTRUCT ps; HWND hwndParent, hwndCtrl; LRESULT lResult; DWORD dwResult; HDC hdc; CHyperLink* _this = (CHyperLink*)GetWindowLongPtr(hwnd, GWLP_USERDATA); _ASSERTE(_this != NULL); switch(uMsg) { case WM_NCHITTEST: return HTCLIENT; case WM_SETCURSOR: _ASSERTE(g_pResManager != NULL); hCursor = g_pResManager->m_hArrowCursor; if (g_pResManager->m_hHandCursor) { dwResult = GetMessagePos(); point.x = GET_X_LPARAM(dwResult); point.y = GET_Y_LPARAM(dwResult); ScreenToClient(hwnd, &point); if (_this->HitTest(point)) hCursor = g_pResManager->m_hHandCursor; } if (hCursor) { SetCursor(hCursor); return TRUE; } return FALSE; case WM_LBUTTONDOWN: if (GetCapture() != hwnd) { x = GET_X_LPARAM(lParam); y = GET_Y_LPARAM(lParam); if (_this->HitTest(x, y)) { _this->m_rgbCurrentColor = m_rgbRedColor; SetCapture(hwnd); if (GetFocus() == hwnd) InvalidateRect(hwnd, NULL, FALSE); else SetFocus(hwnd); } } return 0; case WM_MOUSEMOVE: if (GetCapture() == hwnd) { x = GET_X_LPARAM(lParam); y = GET_Y_LPARAM(lParam); rgbNewColor = _this->HitTest(x, y) ? m_rgbRedColor : m_rgbBlueColor; if (_this->m_rgbCurrentColor != rgbNewColor) { _this->m_rgbCurrentColor = rgbNewColor; InvalidateRect(hwnd, NULL, FALSE); } } return 0; case WM_LBUTTONUP: if (GetCapture() == hwnd) { ReleaseCapture(); x = GET_X_LPARAM(lParam); y = GET_Y_LPARAM(lParam); if (_this->HitTest(x, y)) _this->DoAction(); } return 0; case WM_SETFOCUS: hwndParent = GetParent(hwnd); if (hwndParent) { nCtrlID = GetDlgCtrlID(hwnd); if (_this->m_nPrevDefButtonID < 0) { lResult = SendMessage(hwndParent, DM_GETDEFID, 0, 0); _this->m_nPrevDefButtonID = HIWORD(lResult) == DC_HASDEFID && LOWORD(lResult) != nCtrlID ? LOWORD(lResult) : -1; } SendMessage(hwndParent, DM_SETDEFID, nCtrlID, 0); if (_this->m_nPrevDefButtonID > 0) { // Sending a DM_SETDEFID message to change the default button will not always // remove the default state border from the first push button. In these cases, // the application should send a BM_SETSTYLE message to change the first push // button border style. hwndCtrl = GetDlgItem(hwndParent, _this->m_nPrevDefButtonID); if (SendMessage(hwndCtrl, WM_GETDLGCODE, 0, 0) & DLGC_DEFPUSHBUTTON) SendMessage(hwndCtrl, BM_SETSTYLE, BS_PUSHBUTTON, TRUE); } } InvalidateRect(hwnd, NULL, FALSE); return 0; case WM_KILLFOCUS: hwndParent = GetParent(hwnd); if (hwndParent && wParam && IsChild(hwndParent, (HWND)wParam)) { if (! (SendMessage((HWND)wParam, WM_GETDLGCODE, 0, 0) & (DLGC_DEFPUSHBUTTON | DLGC_UNDEFPUSHBUTTON))) { if (_this->m_nPrevDefButtonID > 0) { nCtrlID = _this->m_nPrevDefButtonID; _this->m_nPrevDefButtonID = -1; } else nCtrlID = IDOK; hwndCtrl = GetDlgItem(hwndParent, nCtrlID); if (hwndCtrl && IsWindowEnabled(hwndCtrl)) SendMessage(hwndParent, DM_SETDEFID, nCtrlID, 0); } else { _this->m_nPrevDefButtonID = -1; nCtrlID = GetDlgCtrlID((HWND)wParam); SendMessage(hwndParent, DM_SETDEFID, nCtrlID, 0); } } InvalidateRect(hwnd, NULL, FALSE); return 0; case WM_CAPTURECHANGED: _this->m_rgbCurrentColor = m_rgbBlueColor; InvalidateRect(hwnd, NULL, FALSE); return 0; case WM_ERASEBKGND: return TRUE; case WM_PAINT: hdc = (HDC)wParam; if (! hdc) { hdc = BeginPaint(hwnd, &ps); if (hdc) { _this->DrawHyperLink(hdc); EndPaint(hwnd, &ps); } } else _this->DrawHyperLink(hdc); return 0; case WM_PRINTCLIENT: hdc = (HDC)wParam; _this->DrawHyperLink(hdc); return 0; case WM_KEYDOWN: switch (wParam) { case VK_RETURN: _this->DoAction(); break; case VK_TAB: hwndParent = GetParent(hwnd); if (hwndParent) { BOOL bPrevCtrl = GetKeyState(VK_SHIFT) < 0; FORWARD_WM_NEXTDLGCTL(hwndParent, bPrevCtrl, FALSE, PostMessage); } break; case VK_LEFT: case VK_UP: hwndParent = GetParent(hwnd); if (hwndParent) FORWARD_WM_NEXTDLGCTL(hwndParent, TRUE, FALSE, PostMessage); break; case VK_RIGHT: case VK_DOWN: hwndParent = GetParent(hwnd); if (hwndParent) FORWARD_WM_NEXTDLGCTL(hwndParent, FALSE, FALSE, PostMessage); break; case VK_ESCAPE: hwndParent = GetParent(hwnd); if (hwndParent) { HWND hwndCtl = GetDlgItem(hwndParent, IDCANCEL); if (! hwndCtl || IsWindowEnabled(hwndCtl)) FORWARD_WM_COMMAND(hwndParent, IDCANCEL, hwndCtl, BN_CLICKED, PostMessage); } break; } return 0; case WM_GETDLGCODE: return ((GetFocus() == hwnd ? DLGC_DEFPUSHBUTTON : DLGC_UNDEFPUSHBUTTON) | DLGC_WANTALLKEYS); case BM_SETSTYLE: return 0; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } } /** * @param hwnd - hyper-link window handle. */ void CHyperLink::Attach(HWND hwnd) { _ASSERTE(hwnd != NULL); _ASSERTE(m_hwnd == NULL); _ASSERTE(g_pResManager != NULL); m_hwnd = hwnd; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)this); m_pfnOldHyperLinkWndProc = SubclassWindow(hwnd, HyperLinkWndProc); InvalidateRect(hwnd, NULL, TRUE); } void CHyperLink::Detach(void) { if (m_pfnOldHyperLinkWndProc) { SubclassWindow(m_hwnd, m_pfnOldHyperLinkWndProc); SetWindowLongPtr(m_hwnd, GWLP_USERDATA, NULL); InvalidateRect(m_hwnd, NULL, TRUE); m_pfnOldHyperLinkWndProc = NULL; m_hwnd = NULL; } } /** * @param size - hyper-link size. */ void CHyperLink::GetHyperLinkSize(SIZE& size) const { HDC hdc = GetDC(m_hwnd); _ASSERTE(g_pResManager != NULL); HFONT hOldFont = NULL; if (g_pResManager->m_hUnderlinedFont) hOldFont = SelectFont(hdc, g_pResManager->m_hUnderlinedFont); TCHAR szLinkText[MAX_PATH]; GetWindowText(m_hwnd, szLinkText, countof(szLinkText)); GetTextExtentPoint32(hdc, szLinkText, (int)_tcslen(szLinkText), &size); if (hOldFont) SelectFont(hdc, hOldFont); ReleaseDC(m_hwnd, hdc); } /** * @param rect - hyper-link rectangle. */ void CHyperLink::GetHyperLinkRect(RECT& rect) const { SIZE size; GetHyperLinkSize(size); rect.left = rect.top = 0; rect.right = size.cx + 3; rect.bottom = size.cy + 3; }