Click here to Skip to main content
15,860,943 members
Articles / Desktop Programming / MFC

Resource ID Organiser Add-In for Visual C++ 5.0/6.0/.NET

Rate me:
Please Sign up or sign in to vote.
4.98/5 (71 votes)
10 Jan 2005CPOL25 min read 527.5K   12.1K   201  
An application/add-in to organise and renumber resource symbol IDs
// XHTMLStatic.cpp  Version 1.0
//
// Author:  Hans Dietrich
//          hdietrich2@hotmail.com
//
// Thanks to Charles Petzold for explaining how GetTextExtentPoint32() works,
// in his excellent "Programming Windows", Fifth Edition:
//     http://www.bookpool.com/.x/6o8gzz6xw6/sm/157231995X
//
// Thanks to Chris Maunder for showing how to set the cursor and receive mouse
// clicks for static controls, and for all the code that I used from his 
// CHyperLink class:
//     http://www.codeproject.com/miscctrl/hyperlink.asp
//
// This software is released into the public domain.
// You are free to use it in any way you like.
//
// This software is provided "as is" with no expressed
// or implied warranty.  I accept no liability for any
// damage or loss of business that this software may cause.
//
///////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "NGHtmlStaticCtrl.h"
#include "NGNamedColours.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


//#define _THEME


#ifdef _THEME
	#include <Uxtheme.h>
	#include <tmschema.h>

#pragma comment(lib, "UxTheme.lib")
#endif

///////////////////////////////////////////////////////////////////////////////
// CNGHtmlStaticCtrl

BEGIN_MESSAGE_MAP(CNGHtmlStaticCtrl, CStatic)

	//{{AFX_MSG_MAP(CNGHtmlStaticCtrl)
	ON_WM_CREATE()
	ON_WM_ERASEBKGND()
	ON_WM_PAINT()
	ON_WM_TIMER()
	ON_WM_SETCURSOR()
	ON_WM_DESTROY()
	//}}AFX_MSG_MAP
    ON_MESSAGE(WM_SETTEXT, OnMsgSetText)
    ON_CONTROL_REFLECT(STN_CLICKED, OnClicked)

END_MESSAGE_MAP()


///////////////////////////////////////////////////////////////////////////////
// ctor
CNGHtmlStaticCtrl::CNGHtmlStaticCtrl()
{
	m_crBackGround		= CLR_DEFAULT;
	m_crText			= ::GetSysColor(COLOR_WINDOWTEXT);
	m_bTransparent		= FALSE;
	m_bUnderline		= FALSE;
	m_bBold				= FALSE;
	m_bItalic			= FALSE;
	m_bStrikeThrough	= FALSE;
	m_bHyperlinkTimer	= FALSE;
	m_bOnHyperlink		= FALSE;
	m_hLinkCursor		= NULL;
	m_hPrevCursor		= NULL;
	m_bInAnchor			= FALSE;
	m_AnchorRectPtrs.RemoveAll();
	m_AnchorUrls.RemoveAll();

    CString strWndDir;
    GetWindowsDirectory(strWndDir.GetBuffer(MAX_PATH), MAX_PATH);
    strWndDir.ReleaseBuffer();

    strWndDir += _T("\\winhlp32.exe");
    // This retrieves cursor #106 from winhlp32.exe, which is a hand pointer
    HMODULE hModule = ::LoadLibrary(strWndDir);
    if (hModule) 
	{
        HCURSOR hHandCursor = ::LoadCursor(hModule, MAKEINTRESOURCE(106));
        if (hHandCursor)
            m_hLinkCursor = CopyCursor(hHandCursor);
    }
    ::FreeLibrary(hModule);
}

///////////////////////////////////////////////////////////////////////////////
// dtor
CNGHtmlStaticCtrl::~CNGHtmlStaticCtrl()
{
	int n = m_AnchorRectPtrs.GetSize();

	for (int i = 0; i < n; i++)
	{
		CRect *pRect = (CRect *) m_AnchorRectPtrs[i];
		if (pRect)
			delete pRect;
	}

	m_AnchorRectPtrs.RemoveAll();
	m_AnchorUrls.RemoveAll();
}


int CNGHtmlStaticCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	int nResult = CStatic::OnCreate(lpCreateStruct);
	if (nResult != -1)
	{
		// We want to get mouse clicks via STN_CLICKED
		DWORD dwStyle = GetStyle();
		::SetWindowLong(GetSafeHwnd(), GWL_STYLE, dwStyle | SS_NOTIFY);

		DWORD dwStyleEx = GetExStyle();

		m_bTransparent = ( (dwStyleEx & WS_EX_TRANSPARENT) > 0);

		CFont* cf = GetFont();
		LOGFONT lf;

		if (cf != NULL)
		{
			cf->GetObject(sizeof(lf), &lf);
		}
		else
		{
			GetObject(GetStockObject(SYSTEM_FONT), sizeof(lf), &lf);
		}

		m_font.DeleteObject();
		VERIFY(m_font.CreateFontIndirect(&lf));
	}
	return nResult;
}


BOOL CNGHtmlStaticCtrl::OnEraseBkgnd(CDC* pDC)
{
	if (!m_bTransparent && (m_crBackGround != CLR_DEFAULT) )
	{
		CRect rect;
		GetClientRect(&rect);

		pDC->FillSolidRect(&rect, m_crBackGround);

		return TRUE;
	}

	return CStatic::OnEraseBkgnd(pDC);
}
   


LRESULT CNGHtmlStaticCtrl::OnMsgSetText(WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);

	Invalidate();

    return Default();
}


void CNGHtmlStaticCtrl::OnPaint() 
{
	CPaintDC dc(this); // device context for painting

	// get text from control
	CString strText;
	GetWindowText(strText);

	// Reset these flags just in case the HTML was badly formed and
	// we were left hanging...
	m_bBold				= FALSE;
	m_bUnderline		= FALSE;
	m_bItalic			= FALSE;
	m_bStrikeThrough	= FALSE;

	CString str1;
	int index;

	// set text and background colors
	COLORREF crText = m_crText;
	COLORREF prev_crText = crText;

	COLORREF crBackground = m_crBackGround;
	COLORREF prev_crBackground = crBackground;

	CFont *pOldFont = dc.SelectObject(&m_font);
	int n = strText.GetLength();


	CRect rect;
	GetClientRect(&rect);

/*	if (!m_bTransparent)
	{
		dc.FillSolidRect(&rect, m_crBackGround);
	}
*/
	int nInitialXOffset = 0;

	LOGFONT lf, prev_lf;
	CFont* cf = GetFont();
	if (cf)
		cf->GetObject(sizeof(lf), &lf);
	else
		GetObject(GetStockObject(SYSTEM_FONT), sizeof(lf), &lf);

	memcpy(&prev_lf, &lf, sizeof(lf));

	CString strAnchorText = _T("");

	while (n > 0)
	{
		///////////////////////////////////////////////////////////////////////
		if (strText.Left(3) == _T("<b>") ||			// check for <b>
			strText.Left(3) == _T("<B>"))			// check for <B>
		{
			n -= 3;
			index = strText.Find(_T('>'));
			if (index != -1)
				strText = strText.Mid(index+1);
			m_bBold++;// = TRUE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (strText.Left(4) == _T("</b>") ||	// check for </b>
				 strText.Left(4) == _T("</B>"))		// check for </B>
		{
			n -= 4;
			index = strText.Find(_T('>'));
			if (index != -1)
				strText = strText.Mid(index+1);
			if (m_bBold)
				m_bBold--;// = FALSE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (strText.Left(3) == _T("<i>") ||	// check for <i>
				 strText.Left(3) == _T("<I>"))		// check for <I>
		{
			n -= 3;
			index = strText.Find(_T('>'));
			if (index != -1)
				strText = strText.Mid(index+1);
			m_bItalic++;// = TRUE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (strText.Left(4) == _T("</i>") ||	// check for </i>
				 strText.Left(4) == _T("</I>"))		// check for </I>
		{
			n -= 4;
			index = strText.Find(_T('>'));
			if (index != -1)
				strText = strText.Mid(index+1);
			if (m_bItalic)
				m_bItalic--;// = FALSE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (strText.Left(3) == _T("<u>") ||	// check for <u>
				 strText.Left(3) == _T("<U>"))		// check for <U>
		{
			n -= 3;
			index = strText.Find(_T('>'));
			if (index != -1)
				strText = strText.Mid(index+1);
			m_bUnderline++;// = TRUE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (strText.Left(4) == _T("</u>") ||	// check for </u>
				 strText.Left(4) == _T("</U>"))		// check for </U>
		{
			n -= 4;
			index = strText.Find(_T('>'));
			if (index != -1)
				strText = strText.Mid(index+1);
			if (m_bUnderline)
				m_bUnderline--;// = FALSE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (strText.Left(8) == _T("<strike>") ||	// check for <strike>
				 strText.Left(8) == _T("<STRIKE>"))		// check for <STRIKE>
		{
			n -= 8;
			index = strText.Find(_T('>'));
			if (index != -1)
				strText = strText.Mid(index+1);
			m_bStrikeThrough++;// = TRUE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (strText.Left(9) == _T("</strike>") ||	// check for </strike>
				 strText.Left(9) == _T("</STRIKE>"))	// check for </STRIKE>
		{
			n -= 9;
			index = strText.Find(_T('>'));
			if (index != -1)
				strText = strText.Mid(index+1);
			if (m_bStrikeThrough)
				m_bStrikeThrough--;// = FALSE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (strText.Left(5) == _T("<font") ||		// check for <font
				 strText.Left(5) == _T("<FONT"))		// check for <FONT
		{
			index = strText.Find(_T('>'));
			if (index != -1)
			{
				CString strFont = strText.Mid(5, index-5);
				int m = strFont.GetLength();
				strText = strText.Mid(index+1);

				// loop to parse FONT attributes
				while (m > 0)
				{
					// trim left whitespace
					if ((strFont.GetLength() > 0) && (strFont[0] == _T(' ')))
					{
						m--;
						strFont = strFont.Mid(1);
						continue;
					}

					///////////////////////////////////////////////////////////
					if (strFont.Left(5) == _T("color") ||
						strFont.Left(5) == _T("COLOR"))
					{
						int index2 = strFont.Find(_T('"'));
						if (index2 != -1)
						{
							m -= index2 + 1;
							strFont = strFont.Mid(index2+1);

							index2 = strFont.Find(_T('"'));
							if (index2 != -1)
							{
								CString strColor = strFont.Left(index2);

								CNGNamedColours nc(strColor);
								crText = nc.GetRGB();
								strFont = strFont.Mid(index2+1);
								m = strFont.GetLength();
							}
						}
						else
							break;
					}
					///////////////////////////////////////////////////////////
					else if (strFont.Left(7) == _T("bgcolor") ||
							 strFont.Left(7) == _T("BGCOLOR"))
					{
						int index2 = strFont.Find(_T('"'));
						if (index2 != -1)
						{
							m -= index2 + 1;
							strFont = strFont.Mid(index2+1);

							index2 = strFont.Find(_T('"'));
							if (index2 != -1)
							{
								CString strBgColor = strFont.Left(index2);
								CNGNamedColours nc(strBgColor);
								crBackground = nc.GetRGB();
								strFont = strFont.Mid(index2+1);
								m = strFont.GetLength();
							}
						}
						else
							break;
					}
					///////////////////////////////////////////////////////////
					else if (strFont.Left(4) == _T("face") ||
							 strFont.Left(4) == _T("FACE"))
					{
						int index2 = strFont.Find(_T('"'));
						if (index2 != -1)
						{
							m -= index2 + 1;
							strFont = strFont.Mid(index2+1);
							index2 = strFont.Find(_T('"'));
							if (index2 != -1)
							{
								memset(lf.lfFaceName, 0, sizeof(lf.lfFaceName));
								_tcsncpy(lf.lfFaceName, strFont, index2);

								m -= index2 + 1;
								if (m > 0)
									strFont = strFont.Mid(index2+1);
								else
									strFont = _T("");
								m = strFont.GetLength();
							}
						}
						else
							break;
					}
					///////////////////////////////////////////////////////////
					else if (strFont.Left(4) == _T("size") ||
							 strFont.Left(4) == _T("SIZE"))
					{
						int index2 = strFont.Find(_T('"'));
						if (index2 != -1)
						{
							m -= index2 + 1;
							strFont = strFont.Mid(index2+1);
							index2 = strFont.Find(_T('"'));
							if (index2 != -1)
							{
								int nSize = 0;
								nSize = _ttoi(strFont);
								lf.lfHeight -= nSize;

								m -= index2 + 1;
								if (m > 0)
									strFont = strFont.Mid(index2+1);
								else
									strFont = _T("");
								m = strFont.GetLength();
							}
						}
						else
							break;
					}
					else
					{
						while ((strFont.GetLength() > 0) && 
							   (strFont[0] != _T(' ')))
						{
							m--;
							strFont = strFont.Mid(1);
						}
					}
				}
				n -= index + 1;
			}
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (strText.Left(7) == _T("</font>") ||	// check for </font>
				 strText.Left(7) == _T("</FONT>"))		// check for </FONT>
		{
			n -= 7;
			index = strText.Find(_T('>'));
			if (index != -1)
				strText = strText.Mid(index+1);
			crText = prev_crText;
			crBackground = prev_crBackground;
			memcpy(&lf, &prev_lf, sizeof(lf));
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		// <a href=www.xyz.com>XYZ Web Site</a>
		else if (strText.Left(8) == _T("<a href=") ||	// check for <a href=
				 strText.Left(8) == _T("<A HREF="))		// check for <A HREF=
		{
			index = strText.Find(_T('>'));
			if (index != -1)
			{
				strAnchorText = strText.Mid(8, index-8);
				strText = strText.Mid(index+1);
				n = strText.GetLength();
				m_bInAnchor = TRUE;
				continue;
			}
		}
		///////////////////////////////////////////////////////////////////////
		else if (strText.Left(4) == _T("</a>") ||	// check for </a>
				 strText.Left(4) == _T("</A>"))		// check for </A>
		{
			strText = strText.Mid(4);
			n -= 4;
			m_bInAnchor = FALSE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		// <br> or \r\n or plain text
		else
		{
			str1 = strText;
			index = str1.Find(_T('<'));
			if (index != -1)
			{
				if (strText.Left(4) == _T("<br>") ||	// check for <br>
					strText.Left(4) == _T("<BR>"))		// check for <BR>
				{
					n -= 4;
					str1 = _T("\r\n ");
					strText = strText.Mid(4);
				}
				else
				{
					str1 = strText.Left(index);
					if (str1.GetLength() <= 0)
					{
						if (strText.GetLength() != 0)
						{
							str1 = strText[0];
							index = 1;
							n -= 1;
						}
					}
					strText = strText.Mid(index);
				}
			}
			else
			{
				str1 = strText;
				strText = _T("");
			}
		}

		lf.lfWeight    = m_bBold ? FW_BOLD : FW_NORMAL;
		lf.lfUnderline = (BYTE) m_bUnderline;
		lf.lfItalic    = (BYTE) m_bItalic;
		lf.lfStrikeOut = (BYTE) m_bStrikeThrough;

		m_font.DeleteObject();
		VERIFY(m_font.CreateFontIndirect(&lf));

		dc.SetTextColor(crText);

		if (m_bTransparent)
		{
			dc.SetBkMode(TRANSPARENT);
		}
		else if (crBackground != CLR_DEFAULT)
		{
			dc.SetBkColor(crBackground);
		}
		dc.SelectObject(&m_font);

		CRect rectDraw;
		rectDraw = rect;

		nInitialXOffset = FormatText(dc.m_hDC, str1, &rect, nInitialXOffset);

		if (m_bInAnchor)
		{
			SIZE size;
			GetTextExtentPoint32(dc.m_hDC, str1, str1.GetLength(), &size);
			rectDraw.bottom = rectDraw.top + size.cy;
			rectDraw.right = rectDraw.left + size.cx;

			// save rect for this text - save in window coordinates
			CRect *pRect = new CRect(rectDraw);

			m_AnchorRectPtrs.Add(pRect);
			m_AnchorUrls.Add(strAnchorText);

			if (!m_bHyperlinkTimer)
			{
				m_bHyperlinkTimer = TRUE;
				SetTimer(1, 80, NULL);
			}
		}

		n -= str1.GetLength();
	}

	// Restore DC's State
	dc.SelectObject(pOldFont);

	// Do not call CStatic::OnPaint() for painting messages
}

///////////////////////////////////////////////////////////////////////////////
// IsBlank
BOOL CNGHtmlStaticCtrl::IsBlank(LPCTSTR lpszText)
{
	TCHAR c;
	while ((c = *lpszText++) != _T('\0'))
		if (c != _T(' ') && c != _T('\t'))
			return FALSE;
	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
// FormatText
int CNGHtmlStaticCtrl::FormatText(HDC hdc, 
							 LPCTSTR lpszText, 
							 RECT * pRect, 
							 int nInitialXOffset)
{
	int		xStart, yStart, nWord, xNext, xLast;
	TCHAR	*pText = (TCHAR *) lpszText;
	TCHAR	*pBegin, *pEnd;
	SIZE	size;

	yStart = pRect->top;
	xNext = nInitialXOffset;
	xLast = 0;

	if (pRect->top >= (pRect->bottom-1))
		return 0;

	// set initial size
	TCHAR * szTest = _T("abcdefgABCDEFG");
	GetTextExtentPoint32(hdc, szTest, _tcslen(szTest), &size);

	// prepare for next line - clear out the error term
	SetTextJustification(hdc, 0, 0);

	CString strOut = _T("");

	BOOL bReturnSeen = FALSE;

	do									// for each text line
	{
		nWord = 0;						// initialize number of spaces in line

		pBegin = pText;					// set pointer to char at beginning of line

		// skip to first non-space in line
		while (*pText != _T('\0') && *pText == _T(' '))
		{
			if (xNext)
				strOut += *pText;
			pText++;
		}

		for(;;)							// until the line is known
		{
			pEnd = pText;				// set pointer to char at end of line

			// skip to next word
			while (*pText != _T('\0') && *pText == _T(' '))
				strOut += *pText++;

			// skip \r
			if (*pText == _T('\r'))
				pText++;
			// \n = new line
			if (*pText == _T('\n'))
			{
				strOut += _T(' ');
				pText++;
				bReturnSeen = TRUE;
			}

			if (!bReturnSeen)
			{
				// skip to next space 
				while (*pText != _T('\0') && *pText != _T(' '))
					strOut += *pText++;
			}

			// after each word, calculate extents

			nWord++;
			GetTextExtentPoint32(hdc, strOut, strOut.GetLength(), &size);

			if (size.cx >= (pRect->right - xNext - 5))	// don't get too close to margin,
														// in case of italic text
			{
				if ((pEnd - pBegin) != 0)
				{
					strOut = strOut.Left(pEnd - pBegin);
					break;
				}
				pText = pBegin;
				xNext = 0;
				yStart += size.cy;
			}
			if (*pText == _T('\0'))
			{
				pEnd = pText;
				strOut = strOut.Left(pEnd - pBegin);
				break;
			}
			if (bReturnSeen)
			{
				pEnd = pText;
				strOut = strOut.Left(pEnd - pBegin);
				break;
			}
		}

		nWord--;               // discount last space at end of line

		// if end of text and no space characters, set pEnd to end

		GetTextExtentPoint32(hdc, strOut, strOut.GetLength(), &size);

		xStart = pRect->left;

		xStart += xNext;
		xNext = 0;
		xLast = xStart + size.cx;

		// display the text

		if ((yStart <= (pRect->bottom-size.cy)) && (!IsBlank(strOut)))
			TextOut(hdc, xStart, yStart, strOut, strOut.GetLength());

		// prepare for next line - clear out the error term
		SetTextJustification(hdc, 0, 0);

		strOut.Empty();

		pText = pEnd;
		if (*pText)
		{
			yStart += size.cy;
		}

		bReturnSeen = FALSE;

	} while (*pText && (yStart < pRect->bottom));

	if (yStart > (pRect->bottom-size.cy))
		pRect->top = pRect->bottom;
	else
		pRect->top = yStart;

	return xLast;
}

///////////////////////////////////////////////////////////////////////////////
// PreSubclassWindow

//
// The override is called when the window is about to be subclassed
// (by DDX_Control or similar)
//
// It is used here to initialise the contents of the window
// This code is needed as OnCreate() will not be called if the window is subclassed!
//
void CNGHtmlStaticCtrl::PreSubclassWindow() 
{
    // We want to get mouse clicks via STN_CLICKED
    DWORD dwStyle = GetStyle();
    ::SetWindowLong(GetSafeHwnd(), GWL_STYLE, dwStyle | SS_NOTIFY);

	CStatic::PreSubclassWindow();

	DWORD dwStyleEx = GetExStyle();

	m_bTransparent = ( (dwStyleEx & WS_EX_TRANSPARENT) > 0);
	
	CFont* cf = GetFont();
	LOGFONT lf;

	if (cf != NULL)
	{
		cf->GetObject(sizeof(lf), &lf);
	}
	else
	{
		GetObject(GetStockObject(SYSTEM_FONT), sizeof(lf), &lf);
	}

	m_font.DeleteObject();
	VERIFY(m_font.CreateFontIndirect(&lf));
}

///////////////////////////////////////////////////////////////////////////////
// OnTimer
void CNGHtmlStaticCtrl::OnTimer(UINT /*nIDEvent*/) 
{
	int n = m_AnchorRectPtrs.GetSize();

	if (n == 0)
		return;

	CPoint point;
	::GetCursorPos(&point);

	m_bOnHyperlink = FALSE;

	for (int i = 0; i < n; i++)
	{
		CRect *pRect = (CRect *) m_AnchorRectPtrs[i];
		CRect rect = *pRect;
		ClientToScreen(&rect);

		if (rect.PtInRect(point))		 // Cursor is currently over control
		{
			if (m_hLinkCursor)
			{
				HCURSOR hPrevCursor = ::SetCursor(m_hLinkCursor);
				if (m_hPrevCursor == NULL)
					m_hPrevCursor = hPrevCursor;
				m_bOnHyperlink = TRUE;
				break;
			}
		}
	}

	if (!m_bOnHyperlink && m_hPrevCursor)
	{
		::SetCursor(m_hPrevCursor);
		m_hPrevCursor = NULL;
		RedrawWindow();
	}
}

///////////////////////////////////////////////////////////////////////////////
// OnSetCursor
BOOL CNGHtmlStaticCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
	if (!m_bOnHyperlink)	
		return CStatic::OnSetCursor(pWnd, nHitTest, message);
	else
		return FALSE;
}

///////////////////////////////////////////////////////////////////////////////
// OnClicked
void CNGHtmlStaticCtrl::OnClicked()
{
	CPoint point;
	::GetCursorPos(&point);

	BOOL bOnHyperlink = FALSE;

	int n = m_AnchorRectPtrs.GetSize();

	if (n == 0)
		return;

	int i;
	for (i = 0; i < n; i++)
	{
		CRect *pRect = (CRect *) m_AnchorRectPtrs[i];
		CRect rect = *pRect;
		ClientToScreen(&rect);

		if (rect.PtInRect(point))		 // Cursor is currently over control
		{
			bOnHyperlink = TRUE;
			break;
		}
	}

	if (bOnHyperlink)
		GotoURL(m_AnchorUrls[i], SW_SHOW);
}

///////////////////////////////////////////////////////////////////////////////
// GoToURL
HINSTANCE CNGHtmlStaticCtrl::GotoURL(LPCTSTR url, int showcmd)
{
	ASSERT(url);
	ASSERT(url[0] != _T('\0'));

	TCHAR key[MAX_PATH + MAX_PATH];

	// First try ShellExecute()
	HINSTANCE result = ShellExecute(NULL, _T("open"), url, NULL,NULL, showcmd);

	// If it failed, get the .htm regkey and lookup the program
	if ((UINT)result <= HINSTANCE_ERROR) 
	{
		if (GetRegKey(HKEY_CLASSES_ROOT, _T(".htm"), key) == ERROR_SUCCESS) 
		{
			_tcscat(key, _T("\\shell\\open\\command"));

			if (GetRegKey(HKEY_CLASSES_ROOT,key,key) == ERROR_SUCCESS) 
			{
				TCHAR *pos;
				pos = _tcsstr(key, _T("\"%1\""));
				if (pos == NULL) {					   // No quotes found
					pos = _tcsstr(key, _T("%1"));	   // Check for %1, without quotes 
					if (pos == NULL)				   // No parameter at all...
						pos = key+lstrlen(key)-1;
					else
						*pos = _T('\0');			   // Remove the parameter
				}
				else
					*pos = _T('\0');				   // Remove the parameter

				_tcscat(pos, _T(" "));
				_tcscat(pos, url);
				result = (HINSTANCE) WinExec((LPSTR)key,showcmd);
			}
		}
	}

	return result;
}

///////////////////////////////////////////////////////////////////////////////
// GetRegKey
LONG CNGHtmlStaticCtrl::GetRegKey(HKEY key, LPCTSTR subkey, LPTSTR retdata)
{
	HKEY hkey;
	LONG retval = RegOpenKeyEx(key, subkey, 0, KEY_QUERY_VALUE, &hkey);
	
	*retdata = 0;

	if (retval == ERROR_SUCCESS) 
	{
		long datasize = MAX_PATH;
		TCHAR data[MAX_PATH];
		retval = RegQueryValue(hkey, NULL, data, &datasize);
		if (retval == ERROR_SUCCESS) 
		{
			lstrcpy(retdata, data);
			RegCloseKey(hkey);
		}
	}
	
	return retval;
}

///////////////////////////////////////////////////////////////////////////////
// OnDestroy
void CNGHtmlStaticCtrl::OnDestroy() 
{
	if (m_bHyperlinkTimer)
		KillTimer(1);
	m_bHyperlinkTimer = FALSE;

	CStatic::OnDestroy();
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Founder Riverblade Limited
United Kingdom United Kingdom
I haven't always written software for a living. When I graduated from Surrey University in 1989, it was with an Electronic Engineering degree, but unfortunately that never really gave me the opportunity to do anything particularly interesting (with the possible exception of designing Darth Vader's Codpiece * for the UK Army in 1990).
    * Also known as the Standard Army Bootswitch. But that's another story...
Since the opportunity arose to lead a software team developing C++ software for Avionic Test Systems in 1996, I've not looked back. More recently I've been involved in the development of subsea acoustic navigation systems, digital TV broadcast systems, port security/tracking systems, and most recently software development tools with my own company, Riverblade Ltd.

One of my personal specialities is IDE plug-in development. ResOrg was my first attempt at a plug-in, but my day to day work is with Visual Lint, an interactive code analysis tool environment with works within the Visual Studio and Eclipse IDEs or on build servers.

I love lots of things, but particularly music, photography and anything connected with history or engineering. I despise ignorant, intolerant and obstructive people - and it shows...I can be a bolshy cow if you wind me up the wrong way...Laugh | :laugh:

I'm currently based 15 minutes walk from the beach in Bournemouth on the south coast of England. Since I moved here I've grown to love the place - even if it is full of grockles in Summer!

Comments and Discussions