Download source files - 3 Kb
Introduction
This article introduces a new cell class for the
MFC Grid Control that provides hyperlinks in cells.
The class
To create the class we simply
derive a cell from CGridCell
. Deriving a cell CGridCell
allows us to maintain the functionality of the normal cell, add add our own url support.
For our new url cell, we will need to handle the draw, click, cursor, URL color, and
URL parsing. We add a COLORREF
variable to store the URL color.
m_clrUrl will store our URL set by SetUrlColor(COLORREF clr)
or the defualt color of GetSysColor(COLOR_HIGHLIGHT)
. To handle the
draw function, we declare:
#include "GridCtrl_Src\GridCell.h"
virtual BOOL Draw(CDC* pDC, int nRow, int nCol, CRect rect, BOOL bEraseBkgnd = TRUE);
We then we parse the cell text for a URL, and if it has a URL we draw the cell with
our highlight color, otherwise we draw the cell as normal. We store the cell rect
before drawing so that we know our bounds when trying to figure out if a URL was
clicked.
BOOL CGridURLCell::Draw(CDC* pDC, int nRow, int nCol, CRect rect, BOOL bEraseBkgnd)
{
if (HasUrl(GetText()))
SetTextClr(m_clrUrl);
m_Rect = rect;
return CGridCell::Draw(pDC, nRow, nCol, rect, bEraseBkgnd);
}
HasUrl(CString str)
searches the cell text for one of the known URL
prefixes. We store the known prefixes in a URLStruct
with the prefixes
and string length. The URLStruct
contains the prefixes in order from
most to least commonly used. HasUrl()
retreives one prefix at a time,
then searches the cell text before retreiving the next prefix. If a URL prefix is found,
then TRUE is returned, else we keep searching and otherwise return FALSE.
URLStruct CGridURLCell::g_szURIprefixes[] = {
{ _T("www."), _tcslen(_T("www.")) },
{ _T("http:"), _tcslen(_T("http:")) },
{ _T("mailto:"), _tcslen(_T("mailto:")) },
{ _T("ftp:"), _tcslen(_T("ftp:")) },
{ _T("ftp."), _tcslen(_T("ftp.")) },
{ _T("https:"), _tcslen(_T("https:")) },
{ _T("news:"), _tcslen(_T("news:")) },
{ _T("gopher:"), _tcslen(_T("gopher:")) },
{ _T("telnet:"), _tcslen(_T("telnet:")) },
{ _T("url:"), _tcslen(_T("url:")) },
{ _T("file:"), _tcslen(_T("file:")) }
};
BOOL CGridURLCell::HasUrl(CString str)
{
int nNumPrefixes = sizeof(g_szURIprefixes) / sizeof(g_szURIprefixes[0]);
for (int i = 0; i < nNumPrefixes; i++)
if (str.Find(g_szURIprefixes[i].szURLPrefix) >= 0)
return TRUE;
return FALSE;
}
To handle the cursor we call GetHandCursor()
in our initialization
and store the result in HCURSOR g_hLinkCursor
. We override OnSetCursor()
so that if we
are over a URL then we show the hand cursor, otherwise the normal cursor if shown. This
allows us to show the normal cursor over our URL cell, if a URL is not present. This is
done by first getting the cursor point, then calling OverURL(CPoint& pt, CString& strURL)
.
OverUrl()
translates the point into cell text, then searches to cell if we clicked a URL
or normal text, and returns TRUE
with the URL in strUrl or else FALSE
with the cell text
in strUrl.
BOOL CGridURLCell::OnSetCursor()
{
#ifndef _WIN32_WCE
CString strURL;
CPoint pt(GetMessagePos());
GetGrid()->ScreenToClient(&pt);
pt = pt - m_Rect.TopLeft();
if (OverURL(pt, strURL))
{
SetCursor(g_hLinkCursor);
return TRUE;
}
else
#endif
return CGridCell::OnSetCursor();
}
#ifndef _WIN32_WCE
HCURSOR CGridURLCell::GetHandCursor()
{
if (g_hLinkCursor == NULL)
{
CString strWndDir;
GetWindowsDirectory(strWndDir.GetBuffer(MAX_PATH), MAX_PATH);
strWndDir.ReleaseBuffer();
strWndDir += _T("\\winhlp32.exe");
HMODULE hModule = LoadLibrary(strWndDir);
if( hModule )
{
HCURSOR hHandCursor = ::LoadCursor(hModule, MAKEINTRESOURCE(106));
if( hHandCursor )
{
g_hLinkCursor = CopyCursor(hHandCursor);
}
}
FreeLibrary(hModule);
}
return g_hLinkCursor;
}
#endif
BOOL CGridURLCell::OverURL(CPoint& pt, CString& strURL)
{
BOOL bOverURL = FALSE;
CSize size = GetTextExtent(GetText());
pt.x += m_Rect.left;
CPoint center = m_Rect.CenterPoint();
if ((m_nFormat & DT_RIGHT) && pt.x >= (m_Rect.right - size.cx))
{
bOverURL = TRUE;
}
else if ((m_nFormat & DT_CENTER) &&
((center.x - (size.cx/2)) <= pt.x) && (pt.x <= (center.x + (size.cx/2))) )
{
bOverURL = TRUE;
}
else if (pt.x <= (size.cx + m_Rect.left))
{
bOverURL = TRUE;
}
if (!bOverURL)
return FALSE;
bOverURL = FALSE;
strURL = GetText();
float width = (float)size.cx/(float)strURL.GetLength();
pt.x -= m_Rect.left;
if (m_nFormat & DT_RIGHT)
{
int wide = m_Rect.Width() - size.cx;
pt.x -= wide;
if (pt.x <= 0)
return FALSE;
}
if (m_nFormat & DT_CENTER)
{
int wide = m_Rect.Width() - size.cx;
pt.x -= (wide/2);
if (pt.x <= 0 || pt.x > (size.cx + (wide/2)))
return FALSE;
}
int ltrs = (int)((float)pt.x/width);
int endSpace = strURL.Find(_T(' '), ltrs);
if (endSpace != -1)
strURL.Delete(endSpace, strURL.GetLength()-endSpace);
int beginSpace = strURL.ReverseFind(_T(' '));
if (beginSpace != -1)
strURL.Delete(0, ++beginSpace);
return HasUrl(strURL);
}
In OnClick(CPoint PointCellRelative)
we check to see if we are
supposed to launch a URL automatically, if so we call OverUrl()
to
retreive the URL to launch, then pass the URL for ShellExecute
to launch the defualt
browser.
void CGridURLCell::OnClick(CPoint PointCellRelative)
{
#ifndef _WIN32_WCE
CString strURL;
if (GetAutoLaunchUrl() && OverURL(PointCellRelative, strURL))
ShellExecute(NULL, _T("open"), strURL, NULL,NULL, SW_SHOW);
#endif
}
GetAutoLaunchUrl()
and SetAutoLaunchUrl(BOOL bLaunch = TRUE)
have been included to allow us to toggle the luanching of URLs. By defualt, URLs are
automatically launched when clicked.
To use the cell
To use the new cell class either set a particular cell's cell-type using
CGridCtrl::SetCellType
, or you can set the entire grid to use the URL cell by
calling CGridCtrl::SetDefCellType
For instance, to set cell (1,1) as a URL cell, you would ensure the CGridURLCell
class header file was included, and then:
m_Grid.SetCellType(1,1, RUNTIME_CLASS(CGridURLCell));
CGridURLCell* cell = (CGridURLCell *)m_Grid.GetCell(1, 1);
cell->...