Při dokončování projektů často programátoři přidávají do aplikace tzv. "About" - dialog se stručnými informacemi o aplikaci a jejím autorovi. Často řeší problém, jak umístit na dialog aktivní hypertextový odkaz, který reaguje na kliknutí myši. Tento článek poradí, jak na to.
Nejdříve si shrneme požadavky, které bude hypertextový odkaz splňovat
Výchozím typem ovládacího prvku bude tlačítko (button), který obsahuje základní funcionalitu, požadovanou od hypertextového odkazu, tedy kliknutí myší. Zároveň bude možné "kliknout" i pomocí klávesnice, stejně jako běžné tlačítko. V resource editoru na dialog umístíme tlačítko, jehož rozměry budou o něco větší než je velikost textu hypertextového odkazu (viz vlevo). Velikost tlačítka určuje aktivní oblast, která bude reagovat na kliknutí. V nastavení tlačítka zvolte vlastnost "owner draw", která nám umožní nakreslit text hypertextového odkazu podle naší vůle. Text tlačítka by měl obsahovat buďto URL adresu, nebo e-mailovou adresu.
Při obsluze zprávy WM_INITDIALOG je nutné dialog předpřipravit pro správnou funkci. Pro vytvoření podtrženého fontu je potřeba nahradit font tlačítka nově vytvořeným fontem. Font bude ve většině případů identický s původním fontem, s výjimkou podtržení. Dalším bodem je načtení kurzoru myši "pacičky", který bude zobrazen při ukázání kurzorem myši. K dosažení této vlastnosti bude nutné tlačítko subclassovat a zachytávat zprávu WM_SETCURSOR. Uvedenou funkcionalitu obsahuje tento kód funkce InitURLButton, který by měla být volána z funkce OnInitDialog (ať již děláte program v MFC nebo v API):
static WNDPROC OldWindowProc; static HCURSOR hCursor = NULL; static LRESULT CALLBACK SetCursorProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { if(msg == WM_SETCURSOR) { SetCursor(hCursor); return 0; } return CallWindowProc(OldWindowProc, hwnd, msg, wParam, lParam); } BOOL IsURLButton(HWND hWnd) { TCHAR szClassName[0x20]; DWORD dwStyle; szClassName[0] = 0; GetClassName(hWnd, szClassName, (sizeof(szClassName) / sizeof(TCHAR)) - 1); dwStyle = GetWindowLong(hWnd, GWL_STYLE); // Must be a button if(_tcsicmp(szClassName, _T("Button"))) return FALSE; // Must not be a groupbox if((dwStyle & BS_GROUPBOX) == BS_GROUPBOX) return FALSE; // If ownerdrawn, the OK. return ((dwStyle & BS_OWNERDRAW) == BS_OWNERDRAW) ? TRUE : FALSE; } // This function sets the font for one URL button. // It must be a button with owner-draw style set. int InitURLButton(HWND hDlg, UINT nIDCtrl, BOOL bBigFont) { LOGFONT logFont; HFONT hFont = NULL; HWND hWnd = GetDlgItem(hDlg, nIDCtrl); // If the button is not there, do nothing. if(hWnd == NULL) return ERROR_FILE_NOT_FOUND; // Retrieve the settings of the dialog font. // The buttons should have the same font like the dialog font. hFont = (HFONT)SendMessage(hDlg, WM_GETFONT, 0, 0); GetObject(hFont, sizeof(LOGFONT), &logFont); // Setup the font logFont.lfUnderline = TRUE; if(bBigFont != FALSE) { logFont.lfHeight = 18; logFont.lfWeight = FW_BOLD; } // Create the font hFont = CreateFontIndirect(&logFont); if(hFont == NULL) return GetLastError(); // Load URL-Point cursor for this buttons hCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_URLPOINT)); if(hCursor == NULL) return GetLastError(); if(IsURLButton(hWnd)) { // Set the new window font and procedure SendMessage(hWnd, WM_SETFONT, (LPARAM)hFont, 0); OldWindowProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)SetCursorProc); } return ERROR_SUCCESS; }
Aby kód fungoval, musíte do resourců vložit kurzor IDC_URLPOINT, který se dá načíst z knihovny mshtml.dll. Protože ale má pokaždé jiný identifikátor, je lepší jej vložit do programu, abychom měli jistotu, že kód bude fungovat na všech verzích Windows.
Protože jsme na dialog přidali tlačítko se stylem "owner draw", budeme si jej muset sami nakreslit. Před nakreslením tlačítka přijde do dialogu zpráva WM_DRAWITEM. Pokud je projekt vytvořen v čistém API, přidejte další case do funkce DialogProc:
int WINAPI DialogProc01(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { ... case WM_DRAWITEM: DrawURLButton(hDlg, (LPDRAWITEMSTRUCT)lParam); break; ... } return FALSE; }
Pokud je projekt vytvořen v MFC, přidejte do mapy zpráv položku pro WM_DRAWITEM:
BEGIN_MESSAGE_MAP(TAboutDlg, CDialog) ON_WM_DRAWITEM() END_MESSAGE_MAP()
V obsluze této zprávy musíte vykreslit text tlačítka. Barva textu se bude lišit podle toho, zda je právě stlačeno tlačítko myši. Verze pro API:
// Draws hypertext button. It is drawn with the blue color // when normal and with the red color when clicked. BOOL DrawURLButton(HWND hDlg, LPDRAWITEMSTRUCT dis) { TCHAR szText[240]; UINT length = 0; if(dis == NULL) return ERROR_INVALID_PARAMETER; if(dis->CtlType == ODT_BUTTON) { // Retrieve text from button length = GetDlgItemText(hDlg, dis->CtlID, szText, (sizeof(szText) / sizeof(TCHAR)) - 1); // Select color and draw it. if(dis->itemState & ODS_SELECTED) SetTextColor(dis->hDC, RGB(0xFF, 0x00, 0x00)); else SetTextColor(dis->hDC, RGB(0x00, 0x00, 0xFF)); TextOut(dis->hDC, 0, 0, szText, length); } return TRUE; }
a verze pro MFC:
// Draw hypertext button. It is drawn with the blue color, // when normal and with the red color when clicked. int TAboutDlg::DrawURLButton(LPDRAWITEMSTRUCT dis) { TCHAR szText[240]; UINT length = 0; if(dis == NULL) return ERROR_INVALID_PARAMETER; if(dis->CtlType == ODT_BUTTON) { // Retrieve text from button length = GetDlgItemText(dis->CtlID, szText, _tsize(szText)); // Select color and draw it. if(dis->itemState & ODS_SELECTED) SetTextColor(dis->hDC, RGB(0xFF, 0x00, 0x00)); else SetTextColor(dis->hDC, RGB(0x00, 0x00, 0xFF)); TextOut(dis->hDC, 0, 0, szText, length); } return ERROR_SUCCESS; }
Při obsluze našeho "WWW" tlačítka postupujeme podobně jako při obsluze kteréhokoliv jiného tlačítka. Při jeho stlačení přijde do obslužné rutiny dialogu zpráva WM_COMMAND s parametry ID tlačítka a kódem zprávy BN_CLICKED. V reakci na tuto zprávu zjistíme text tlačítka a použijeme jej jako parametr funkce ShellExecute, která zajistí buďto spuštění aktuálně nainstalovaného webového prohlížeče nebo mailového klienta:
// This callback handles the mouse click on an URL button (WWW, mail, http). // It invokes the default web browser/mail client for the button // (Recognized from button text) BOOL ClickURLButton(HWND hURL) { HCURSOR hWaitCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_URLWAIT)); TCHAR szUrl[128] = _T(""); TCHAR szText[128]; // Retrieve button text GetWindowText(hURL, szText, (sizeof(szText) / sizeof(TCHAR)) - 1); // Test E-mail addresses. They must have "mailto:" at the begin if(_tcschr(szText, _T('@')) != NULL) { _tcscpy(szUrl, _T("mailto:")); _tcscat(szUrl, szText); } // Test "http://" if(!_tcsnicmp(szText, _T("http://"), 7)) _tcscpy(szUrl, szText); // If only "www." at the begin, we have to add "http://" if(!_tcsnicmp(szText, _T("www."), 4)) { _tcscpy(szUrl, _T("http://")); _tcscat(szUrl, szText); } // If any valid URL address, invoke the default application if(szUrl[0] != 0) { // Set waiting cursor and launch default browser SetCursor(hWaitCursor); ShellExecute(NULL, _T("open"), szUrl, NULL, NULL, SW_SHOWNORMAL); SetCursor(hCursor); } return TRUE; }
Jakmile program přeložíme a spustíme, bude hypertextový odkaz vypadat takto:
Výsledný příklad si můžete stáhnout a vyzkoušet. Příklad je psaný v API, programátoři by jej měli být schopni přepsat tak, aby se dal zařadit do projektu napsaného i v MFC.
Copyright (c) Ladislav Zezula 22.03.2004