www.pudn.com > RichTextEx.rar > HyperTextCtrl.h


/******************************************************************** 
 
 
		HyperTextCtrl.h - Controls that shows hyperlinks  
		in text 
 
		Copyright (C) 2001-2002 Magomed G. Abdurakhmanov			 
		 
		History: 
 
		11.06.2002 
			+ CHyperTextCtrlFSB - uses flat scroll bars 
			+ SetTextColor, SetBkColor 
			- bugs fixed 
 
		08.06.2002 
			+ Now CHyperTextCtrl inherits from CHyperTextCtrlT<> 
 
		07.06.2002	 
			+ .cpp file removed. Now all code is in HyperTextCtrl.h 
			- bugs fixed  
			* Tested on WinXP, VC++7, WTL7 
 
		01.06.2001	 
			+ Control was created.  
			* Tested on Win2K, VC++6 SP3, WTL3.1 
 
 
********************************************************************/ 
 
#ifndef MAQ_HYPERTEXTCONTROL_H 
#define MAQ_HYPERTEXTCONTROL_H 
 
#if _MSC_VER > 1000 
#pragma once 
#endif 
 
#include  
#include  
 
namespace HyperTextControl  
{ 
 
#define HTC_WORDWRAP			1	// word wrap text 
#define HTC_AUTO_SCROLL_BARS	2	// auto hide scroll bars 
#define HTC_UNDERLINE_LINKS		4	// underline links 
#define HTC_UNDERLINE_HOVER		8	// underline hover links 
#define HTC_ENABLE_TOOLTIPS		16	// enable hyperlink tool tips 
 
// -------------------------------------------------------------- 
// CHyperLink 
  
class CHyperLink 
{ 
	int m_iBegin; 
	int m_iEnd; 
	CString m_sTitle; 
 
	enum LinkType 
	{ 
		lt_Shell = 0, /* http:// mailto:*/ 
		lt_Message = 1 /* WM_COMMAND */ 
	} m_Type; 
 
	// used for lt_Shell 
	CString m_sCommand; 
	CString m_sDirectory; 
 
	// used for lt_Message 
	HWND m_hWnd;  
	UINT m_uMsg; 
	WPARAM m_wParam; 
	LPARAM m_lParam; 
 
public: 
 
	CHyperLink(int iBegin, int iEnd, const CString& sTitle, const CString& sCommand, const CString& sDirectory) 
	{ 
		m_Type = lt_Shell; 
		m_iBegin = iBegin; 
		m_iEnd = iEnd; 
		m_sTitle = sTitle; 
		m_sCommand = sCommand; 
		m_sDirectory = sDirectory; 
	} 
 
	CHyperLink(int iBegin, int iEnd, const CString& sTitle, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
	{ 
		m_Type = lt_Message; 
		m_iBegin = iBegin; 
		m_iEnd = iEnd; 
		m_sTitle = sTitle; 
		m_hWnd = hWnd; 
		m_uMsg = uMsg; 
		m_wParam = wParam; 
		m_lParam = lParam; 
	} 
 
	CHyperLink(const CHyperLink& Src) 
	{ 
		m_Type = Src.m_Type; 
		m_iBegin = Src.m_iBegin; 
		m_iEnd = Src.m_iEnd; 
		m_sTitle = Src.m_sTitle; 
		m_sCommand = Src.m_sCommand; 
		m_sDirectory = Src.m_sDirectory; 
		m_hWnd = Src.m_hWnd; 
		m_uMsg = Src.m_uMsg; 
		m_wParam = Src.m_wParam; 
		m_lParam = Src.m_lParam; 
	} 
 
	void Execute() 
	{ 
		switch(m_Type) 
		{ 
		case lt_Shell: 
			ShellExecute(NULL, NULL, m_sCommand, NULL, m_sDirectory, SW_SHOWDEFAULT); 
			break; 
 
		case lt_Message: 
			PostMessage(m_hWnd, m_uMsg, m_wParam, m_lParam); 
			break; 
		} 
	} 
 
	inline bool operator < (const CHyperLink& Arg) 
	{ 
		return m_iEnd < Arg.m_iEnd; 
	} 
 
	inline int Begin() 
	{ 
		return m_iBegin; 
	} 
 
	inline int End() 
	{ 
		return m_iEnd; 
	} 
 
	inline int Len() 
	{ 
		return m_iEnd - m_iBegin + 1; 
	} 
 
	inline CString Title() 
	{ 
		return m_sTitle; 
	} 
 
	friend class CPreparedHyperText; 
}; 
 
// -------------------------------------------------------------- 
// CPreparedHyperText 
 
class CPreparedHyperText 
{ 
protected: 
	CString m_sText; 
	std::list m_Links; 
 
	inline void RemoveLastSign(CString& sLink) 
	{ 
		int len = sLink.GetLength(); 
		if(len > 0) 
		{ 
			TCHAR c = sLink[len-1]; 
			switch(c) 
			{ 
				case _T('.'): 
				case _T(','): 
				case _T(';'): 
				case _T('\"'): 
				case _T('\''): 
				case _T('('): 
				case _T(')'): 
				case _T('['): 
				case _T(']'): 
				case _T('{'): 
				case _T('}'): 
				sLink.Delete(len -1, 1); 
				break; 
			} 
		} 
	} 
 
	void PrepareText(const CString& sText) 
	{ 
		m_sText = sText; 
		m_Links.clear(); 
		 
		enum { 
			unknown, 
			space, 
			http0,		/* http://		*/ 
				http1, http2, http3, http4, http5, http6, 
			ftp0,		/* ftp://		*/ 
				ftp1, ftp2, ftp3, ftp4, ftp5, 
			ftp,		/* ftp.			*/ 
			www0,		/* www.			*/ 
				www1, www2, www3, 
			mailto0, 	/* mailto:		*/ 
				mailto1, mailto2, mailto3, mailto4, mailto5, mailto6, 
			mail		/* xxx@yyy		*/ 
		} state = space; 
		 
		int WordPos = 0; 
		TCHAR sz[2]; 
		TCHAR& c = sz[0]; 
		sz[1] = 0; 
		int last = m_sText.GetLength() -1; 
		for(int i = 0; i <= last; i++) 
		{ 
			c = m_sText[i]; 
			_tcslwr(sz); 
 
			switch(state) 
			{ 
			case unknown: 
				if(tspace(c)) 
					state = space; 
				else 
				if(c == _T('@') && WordPos != i) 
					state = mail;		 
				break; 
 
			case space: 
				WordPos = i; 
				switch(c) 
				{ 
				case _T('h'): state = http0; break; 
				case _T('f'): state = ftp0; break; 
				case _T('w'): state = www0; break; 
				case _T('m'): state = mailto0; break; 
				default: 
					if(!tspace(c)) 
						state = unknown; 
				} 
				break; 
 
			/*----------------- http -----------------*/ 
			case http0: 
				if(c == _T('t')) 
					state = http1; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case http1: 
				if(c == _T('t')) 
					state = http2; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case http2: 
				if(c == _T('p')) 
					state = http3; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case http3: 
				if(c == _T(':')) 
					state = http4; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case http4: 
				if(c == _T('/')) 
					state = http5; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case http5: 
				if(c == _T('/')) 
					state = http6; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case http6: 
				if(tspace(c) || i == last) 
				{ 
					int len = i == last ? i - WordPos + 1 : i - WordPos; 
					CString s = m_sText.Mid(WordPos, len); 
					RemoveLastSign(s); 
					m_Links.push_back(CHyperLink(WordPos, WordPos + len - 1, s, s, (LPCTSTR)NULL)); 
					state = space; 
				} 
				break; 
 
			/*----------------- ftp -----------------*/ 
			case ftp0: 
				if(c == _T('t')) 
					state = ftp1; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case ftp1: 
				if(c == _T('p')) 
					state = ftp2; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case ftp2: 
				if(c == _T(':')) 
					state = ftp3; 
				else 
				if(c == _T('.')) 
					state = ftp; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case ftp3: 
				if(c == _T('/')) 
					state = ftp4; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case ftp4: 
				if(c == _T('/')) 
					state = ftp5; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case ftp: 
				if(tspace(c) || i == last) 
				{ 
					int len = i == last ? i - WordPos + 1 : i - WordPos; 
					CString s = CString(_T("ftp://")) + m_sText.Mid(WordPos, len); 
					RemoveLastSign(s); 
					m_Links.push_back(CHyperLink(WordPos, WordPos + len - 1, s, s, (LPCTSTR)NULL)); 
					state = space; 
				} 
				break; 
 
			case ftp5: 
				if(tspace(c) || i == last) 
				{ 
					int len = i == last ? i - WordPos + 1 : i - WordPos; 
					CString s = m_sText.Mid(WordPos, len); 
					RemoveLastSign(s); 
					m_Links.push_back(CHyperLink(WordPos, WordPos + len - 1, s, s, (LPCTSTR)NULL)); 
					state = space; 
				} 
				break; 
 
			/*----------------- www -----------------*/ 
			case www0: 
				if(c == _T('w')) 
					state = www1; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case www1: 
				if(c == _T('w')) 
					state = www2; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case www2: 
				if(c == _T('.')) 
					state = www3; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case www3: 
				if(tspace(c) || i == last) 
				{ 
					int len = i == last ? i - WordPos + 1 : i - WordPos; 
					CString s = CString(_T("http://")) + m_sText.Mid(WordPos, len); 
					RemoveLastSign(s); 
					m_Links.push_back(CHyperLink(WordPos, WordPos + len - 1, s, s, (LPCTSTR)NULL)); 
					state = space; 
				} 
				break; 
 
			/*----------------- mailto -----------------*/ 
			case mailto0: 
				if(c == _T('a')) 
					state = mailto1; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case mailto1: 
				if(c == _T('i')) 
					state = mailto2; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case mailto2: 
				if(c == _T('l')) 
					state = mailto3; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case mailto3: 
				if(c == _T('t')) 
					state = mailto4; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case mailto4: 
				if(c == _T('o')) 
					state = mailto5; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case mailto5: 
				if(c == _T(':')) 
					state = mailto6; 
				else 
				if(tspace(c)) 
					state = space; 
				else 
					state = unknown; 
				break; 
 
			case mailto6: 
				if(tspace(c) || i == last) 
				{ 
					int len = i == last ? i - WordPos + 1 : i - WordPos; 
					CString s = m_sText.Mid(WordPos, len); 
					RemoveLastSign(s); 
					m_Links.push_back(CHyperLink(WordPos, WordPos + len - 1, s, s, (LPCTSTR)NULL)); 
					state = space; 
				} 
				break; 
 
			/*----------------- mailto -----------------*/ 
			case mail: 
				if(tspace(c) || i == last) 
				{ 
					int len = i == last ? i - WordPos + 1 : i - WordPos; 
					CString s = CString(_T("mailto:")) + m_sText.Mid(WordPos, len); 
					RemoveLastSign(s); 
					m_Links.push_back(CHyperLink(WordPos, WordPos + len - 1, s, s, (LPCTSTR)NULL)); 
					state = space; 
				} 
				break; 
			} 
		} 
 
		m_Links.sort(); 
	} 
 
	bool tspace(TCHAR c) 
	{ 
		return _istspace(c) || c < _T(' ') || c == _T(';') || c == _T(',') || c == _T('!'); 
	} 
 
public: 
 
	CPreparedHyperText() 
	{ 
	} 
 
	CPreparedHyperText(const CString& sText) 
	{ 
		PrepareText(sText); 
	} 
 
	CPreparedHyperText(const CPreparedHyperText& src) 
	{ 
		m_sText = src.m_sText; 
		m_Links.assign(src.m_Links.begin(), src.m_Links.end()); 
	} 
 
	void Clear() 
	{ 
		m_sText.Empty(); 
		m_Links.erase(m_Links.begin(), m_Links.end()); 
	} 
 
	void SetText(const CString& sText) 
	{ 
		Clear(); 
		PrepareText(sText); 
	} 
 
	void AppendText(const CString& sText) 
	{ 
		int len = m_sText.GetLength(); 
		CPreparedHyperText ht(sText); 
		m_sText+=sText; 
		for(std::list::iterator it = ht.m_Links.begin(); it != ht.m_Links.end(); it++) 
		{ 
			CHyperLink hl = *it; 
			hl.m_iBegin += len; 
			hl.m_iEnd += len; 
			m_Links.push_back(hl); 
		} 
	} 
 
	void AppendHyperLink(const CString& sText, const CString& sTitle, const CString& sCommand, const CString& sDirectory) 
	{ 
		ATLASSERT(sText.GetLength()>0); 
		ATLASSERT(sCommand.GetLength()>0); 
 
		int len = m_sText.GetLength(); 
		m_sText+=sText; 
		m_Links.push_back(CHyperLink(len, len + sText.GetLength() - 1, sTitle, sCommand, sDirectory)); 
	} 
 
	void AppendHyperLink(const CString& sText, const CString& sTitle, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
	{ 
		ATLASSERT(sText.GetLength()>0); 
 
		int len = m_sText.GetLength(); 
		m_sText+=sText; 
		m_Links.push_back(CHyperLink(len, len + sText.GetLength() - 1, sTitle, hWnd, uMsg, wParam, lParam)); 
	} 
 
	inline CString& GetText() 
	{ 
		return m_sText; 
	} 
 
	inline std::list& GetLinks() 
	{ 
		return m_Links; 
	} 
	//friend class CHyperTextCtrl; 
}; 
 
// -------------------------------------------------------------- 
// CHyperTextCtrlT 
 
template  
class CHyperTextCtrlT : public T 
{ 
protected: 
	class CLinePartInfo 
	{ 
	public: 
		int m_iBegin; 
		int m_iEnd; 
		CHyperLink* m_pHyperLink; 
 
		inline CLinePartInfo(int iBegin, int iEnd, CHyperLink* pHyperLink = NULL) 
		{ 
			m_iBegin = iBegin; 
			m_iEnd = iEnd; 
			m_pHyperLink = pHyperLink; 
		} 
 
		inline CLinePartInfo(const CLinePartInfo& Src) 
		{ 
			m_iBegin = Src.m_iBegin; 
			m_iEnd = Src.m_iEnd; 
			m_pHyperLink = Src.m_pHyperLink; 
		} 
 
		inline int Begin() 
		{ 
			return m_iBegin; 
		} 
 
		inline int End() 
		{ 
			return m_iEnd; 
		} 
 
		inline int Len() 
		{ 
			return m_iEnd - m_iBegin + 1; 
		} 
	}; 
 
	class CLineInfo : public std::vector 
	{ 
	public: 
		int m_iBegin; 
		int m_iEnd; 
 
		inline CLineInfo(int iBegin, int iEnd) 
		{ 
			m_iBegin = iBegin; 
			m_iEnd = iEnd; 
		} 
 
		inline CLineInfo(const CLineInfo& Src) 
		{ 
			m_iBegin = Src.m_iBegin; 
			m_iEnd = Src.m_iEnd; 
			assign(Src.begin(), Src.end()); 
		} 
 
		inline int Begin() 
		{ 
			return m_iBegin; 
		} 
 
		inline int End() 
		{ 
			return m_iEnd; 
		} 
 
		inline int Len() 
		{ 
			return m_iEnd - m_iBegin + 1; 
		} 
	}; 
 
	class CVisPart : public CLinePartInfo 
	{ 
	public: 
		CRect m_rcBounds; 
		int m_iRealBegin; 
		int m_iRealLen; 
		CVisPart* m_pPrev; 
		CVisPart* m_pNext; 
 
		inline CVisPart( 
				const CLinePartInfo& LinePartInfo,  
				const CRect& rcBounds,  
				int iRealBegin,  
				int iRealLen, 
				CVisPart* pPrev, 
				CVisPart* pNext 
			) : CLinePartInfo(LinePartInfo) 
		{ 
			m_rcBounds = rcBounds; 
			m_iRealBegin = iRealBegin; 
			m_iRealLen = iRealLen; 
			m_pPrev = pPrev; 
			m_pNext = pNext; 
		} 
 
		inline CVisPart(const CVisPart& Src) : CLinePartInfo(Src) 
		{ 
			m_rcBounds = Src.m_rcBounds; 
			m_iRealBegin = Src.m_iRealBegin; 
			m_iRealLen = Src.m_iRealLen; 
			m_pPrev = Src.m_pPrev; 
			m_pNext = Src.m_pNext; 
		} 
	}; 
 
	class CVisLine : public std::vector 
	{	}; 
 
 
	CPreparedHyperText m_Text; 
	std::vector m_Lines;	 
	CFontHandle m_Font; 
	COLORREF m_BkColor; 
	COLORREF m_TextColor; 
	COLORREF m_LinkColor; 
	COLORREF m_HoverColor; 
	HCURSOR m_LinkCursor; 
	HCURSOR m_DefaultCursor; 
 
	CToolTipCtrl m_tip; 
 
	//temporary variables 
	int m_iMaxWidth;				// The maximum line width 
	int m_iLineHeight;				// Height of one line 
	int m_iLinesHeight;				// Sum of height of all lines 
	bool m_bDontUpdateSizeInfo;		// Used to prevent recursive call of the UpdateSize() method 
	int m_iVertPos;					// Vertical position in percents 
	int m_iHorzPos;					// Horizontal position in percents 
	CFont m_DefaultFont;			// This font is set by default 
	CFont m_LinksFont;				// Copied from main font to faster draw (link) 
	CFont m_HoverFont;				// Copied from main font to faster draw (hover link) 
	std::vector m_VisLines;	// Currently visible text 
	CVisPart* m_pActivePart;		// Active part of link (hovered) 
	int m_iWheelDelta;				// Mouse wheel scroll delta 
 
	/*void Activate() 
	{ 
		if(GetParent() == NULL) 
			SetActiveWindow(); 
		else 
		{ 
			HWND hwndParent = GetParent(); 
			HWND hwndParentNext = ::GetParent(hwndParent); 
			while(hwndParentNext != NULL) 
			{ 
				hwndParent = hwndParentNext; 
				hwndParentNext = ::GetParent(hwndParent); 
			} 
 
			::SetActiveWindow(hwndParent); 
		} 
	}*/ 
 
public: 
	virtual BOOL PreTranslateMessage(MSG* pMsg) 
	{ 
		return FALSE; 
	} 
 
	//message handlers 
	LRESULT OnCreate(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
	{     
		LPCREATESTRUCT lpCreateStruct = (LPCREATESTRUCT)lParam; 
		m_iMaxWidth = 0; 
		m_iLinesHeight = 0; 
		m_bDontUpdateSizeInfo = false; 
		m_iHorzPos = 0; 
		m_iVertPos = 0; 
		m_DefaultFont.CreatePointFont(100,_T("Times New Roman")); 
		m_Font = m_DefaultFont; 
		m_LinkColor = RGB(0,0,255); 
		m_HoverColor = RGB(255,0,0); 
		m_BkColor = GetSysColor(COLOR_WINDOW); 
		m_TextColor = GetSysColor(COLOR_WINDOWTEXT); 
		m_LinkCursor = LoadCursor(NULL,IDC_ARROW); 
		m_DefaultCursor = LoadCursor(NULL,IDC_ARROW); 
		m_pActivePart = NULL; 
		m_iWheelDelta = 0; 
 
		// create a tool tip 
		m_tip.Create(*this); 
		ATLASSERT(m_tip.IsWindow()); 
		if(m_tip.IsWindow()) 
			m_tip.Activate(TRUE); 
 
		UpdateFonts(); 
		return 0; 
	} 
 
	LRESULT OnPaint(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
	{ 
		CPaintDC dc(m_hWnd); // device context for painting 
 
		CFontHandle hOldFont = dc.SelectFont(m_Font); 
		 
		dc.SetBkColor(m_BkColor); 
 
		int ypos = 0; 
		LPCTSTR s = m_Text.GetText(); 
 
		CRect rc; 
		CRect rcClient; 
		GetClientRect(rcClient); 
		rc.left = dc.m_ps.rcPaint.left; 
		rc.right = 2; 
		rc.top = dc.m_ps.rcPaint.top; 
		rc.bottom = dc.m_ps.rcPaint.bottom; 
 
		CBrush brBk; 
		brBk.CreateSolidBrush(m_BkColor); 
		dc.FillRect(rc, brBk); 
 
		for(std::vector::iterator it = m_VisLines.begin(); it != m_VisLines.end(); it++) 
		{ 
			int iLastX = dc.m_ps.rcPaint.left; 
			for(CVisLine::iterator jt = it->begin(); jt != it->end(); jt++) 
			{ 
				if(jt->m_pHyperLink == NULL) 
					dc.SetTextColor(m_TextColor); 
				else 
				{ 
					if(m_pActivePart != NULL && m_pActivePart->m_pHyperLink == jt->m_pHyperLink) 
					{ 
						dc.SetTextColor(m_HoverColor); 
						dc.SelectFont(m_HoverFont); 
					} 
					else 
					{ 
						dc.SetTextColor(m_LinkColor); 
						dc.SelectFont(m_LinksFont); 
					} 
				} 
				 
				TextOut(dc, jt->m_rcBounds.left, jt->m_rcBounds.top, s + jt->m_iRealBegin, jt->m_iRealLen); 
 
				if(jt->m_pHyperLink != NULL) 
					dc.SelectFont(m_Font); 
 
				iLastX = jt->m_rcBounds.right; 
			} 
 
			rc.left = iLastX; 
			rc.right = dc.m_ps.rcPaint.right; 
			rc.top = ypos; 
			rc.bottom = ypos + m_iLineHeight; 
 
			dc.FillRect(rc, brBk); 
 
			ypos+=m_iLineHeight; 
		} 
 
		rc.left = dc.m_ps.rcPaint.left; 
		rc.right = dc.m_ps.rcPaint.right; 
		rc.top = ypos; 
		rc.bottom = dc.m_ps.rcPaint.bottom; 
		dc.FillRect(rc, brBk); 
 
		dc.SelectFont(hOldFont); 
		return 0; 
	} 
 
	LRESULT OnSize(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
	{ 
		WORD cx, cy; 
		cx = LOWORD(lParam); 
		cy = HIWORD(lParam); 
 
		UpdateSize(IsWindowVisible() == TRUE); 
		return 0; 
	} 
 
	LRESULT OnShowWindow(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
	{ 
		if(TRUE == (BOOL)wParam) 
			UpdateSize(false); 
		return 0; 
	} 
 
	LRESULT OnSetText(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
	{ 
		m_Text.SetText((LPTSTR)lParam); 
		UpdateSize(IsWindowVisible() == TRUE); 
		return TRUE; 
	} 
 
	LRESULT OnGetText(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
	{ 
		int bufsize = wParam; 
		LPTSTR buf = (LPTSTR)lParam; 
		if(lParam == NULL || bufsize == 0 || m_Text.GetText().IsEmpty()) 
			return 0; 
		int cpy = m_Text.GetText().GetLength() > (bufsize-1) ? (bufsize-1) : m_Text.GetText().GetLength(); 
		_tcsncpy(buf, m_Text.GetText(), cpy); 
		return cpy; 
	} 
 
	LRESULT OnSetFont(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
	{ 
		m_Font = (HFONT)wParam; 
		UpdateFonts(); 
		UpdateSize(LOWORD(lParam) != 0); 
		return 0; 
	} 
 
	LRESULT OnGetFont(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
	{ 
		return (LRESULT)m_Font.m_hFont; 
	} 
 
	LRESULT OnHScroll(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
	{ 
		SCROLLINFO si; 
		si.cbSize = sizeof(si); 
		si.fMask = SIF_ALL; 
 
		GetScrollInfo(SB_HORZ, &si); 
 
		switch(LOWORD(wParam)) 
		{ 
		case SB_LEFT: 
			si.nPos=si.nMin; 
			break; 
 
		case SB_RIGHT: 
			si.nPos=si.nMax; 
			break; 
 
		case SB_LINELEFT: 
			if(si.nPos > si.nMin) 
				si.nPos-=1; 
			break; 
 
		case SB_LINERIGHT: 
			if(si.nPos < si.nMax) 
				si.nPos+=1; 
			break; 
 
		case SB_PAGELEFT: 
			if(si.nPos > si.nMin) 
				si.nPos-=si.nPage; 
			if(si.nPos < si.nMin) 
				si.nPos = si.nMin; 
			break; 
 
		case SB_PAGERIGHT: 
			if(si.nPos < si.nMax) 
				si.nPos+=si.nPage; 
			if(si.nPos > si.nMax) 
				si.nPos = si.nMax; 
			break; 
 
		case SB_THUMBTRACK: 
			si.nPos=si.nTrackPos; 
			break; 
		} 
			 
		if(si.nMax != si.nMin) 
			m_iHorzPos = si.nPos * 100 / (si.nMax - si.nMin); 
		SetScrollInfo(SB_HORZ, &si); 
		UpdateVisLines(); 
		InvalidateRect(NULL,FALSE); 
		return TRUE; 
	} 
 
	LRESULT OnVScroll(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
	{ 
		SCROLLINFO si; 
		si.cbSize = sizeof(si); 
		si.fMask = SIF_ALL; 
		GetScrollInfo(SB_VERT, &si); 
 
		switch(LOWORD(wParam)) 
		{ 
		case SB_TOP: 
			si.nPos=si.nMin; 
			break; 
 
		case SB_BOTTOM: 
			si.nPos=si.nMax; 
			break; 
 
		case SB_LINEUP: 
			if(si.nPos > si.nMin) 
				si.nPos-=1; 
			break; 
 
		case SB_LINEDOWN: 
			if(si.nPos < si.nMax) 
				si.nPos+=1; 
			break; 
 
		case SB_PAGEUP: 
			if(si.nPos > si.nMin) 
				si.nPos-=si.nPage; 
			if(si.nPos < si.nMin) 
				si.nPos = si.nMin; 
			break; 
 
		case SB_PAGEDOWN: 
			if(si.nPos < si.nMax) 
				si.nPos+=si.nPage; 
			if(si.nPos > si.nMax) 
				si.nPos = si.nMax; 
			break; 
 
		case SB_THUMBTRACK: 
			si.nPos=si.nTrackPos; 
			break; 
		} 
			 
		if(si.nMax != si.nMin) 
			m_iVertPos = si.nPos * 100 / (si.nMax - si.nMin); 
		SetScrollInfo(SB_VERT, &si); 
		UpdateVisLines(); 
		InvalidateRect(NULL,FALSE); 
		return TRUE; 
	} 
 
	LRESULT OnMouseMessage(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
	{    
		if(Msg == WM_MOUSEMOVE) 
		{ 
			CPoint pt(LOWORD(lParam),HIWORD(lParam)); 
			CRect rcClient; 
			GetClientRect(rcClient); 
			if(PtInRect(rcClient, pt)) 
			{ 
				bool bFound = false; 
				if(GetCapture() != m_hWnd) 
					SetCapture(); 
 
				unsigned int i = pt.y / m_iLineHeight; 
				if(i < m_VisLines.size()) 
				{ 
					std::vector::iterator it = m_VisLines.begin() + i; 
					for(CVisLine::iterator jt = it->begin(); jt != it->end(); jt++) 
						if(pt.x >= jt->m_rcBounds.left && pt.x <= jt->m_rcBounds.right) 
						{ 
							if(jt->m_pHyperLink != NULL) 
							{ 
								HighlightLink(&*jt, pt); 
								bFound = true; 
							} 
							break; 
						} 
				} 
				if(!bFound) 
					RestoreLink(); 
			} 
			else 
				ReleaseCapture(); 
		} 
		else 
		if(Msg == WM_LBUTTONDOWN) 
		{ 
			CPoint pt(LOWORD(lParam),HIWORD(lParam)); 
			CRect rcClient; 
			GetClientRect(rcClient); 
			if(PtInRect(rcClient, pt)) 
			{ 
				bool bFound = false; 
				unsigned int i = pt.y / m_iLineHeight; 
				if(i < m_VisLines.size()) 
				{ 
					std::vector::iterator it = m_VisLines.begin() + i; 
					for(CVisLine::iterator jt = it->begin(); jt != it->end(); jt++) 
						if(pt.x >= jt->m_rcBounds.left && pt.x <= jt->m_rcBounds.right) 
						{ 
							if(jt->m_pHyperLink != NULL) 
							{ 
								jt->m_pHyperLink->Execute(); 
								bFound = true; 
							} 
							break; 
						} 
				} 
 
				/*if(!bFound) 
					Activate();*/ 
			} 
		} 
		#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) 
		else 
		if(Msg == WM_MOUSEWHEEL) 
		{ 
			CPoint pt(LOWORD(lParam),HIWORD(lParam)); 
			CRect rc; 
			GetWindowRect(rc); 
			if(PtInRect(rc, pt)) 
			{ 
				int iScrollLines; 
				SystemParametersInfo(SPI_GETWHEELSCROLLLINES,  
									0,  
									&iScrollLines,  
									0); 
 
				m_iWheelDelta -= (short)HIWORD(wParam); 
				if(abs(m_iWheelDelta) >= WHEEL_DELTA) 
				{ 
					if(m_iWheelDelta > 0) 
					{ 
						for(int i = 0; i 5 && rc.Height() > 5) 
		{ 
			std::list::iterator it = m_Text.GetLinks().begin(); 
			LPCTSTR s = m_Text.GetText(); 
			int len = m_Text.GetText().GetLength(); 
			int width = rc.Width(); 
			 
			int npos, /* new position */ 
				pos = 0, /* current position */ 
				ll, /* line length */ 
				rll; /* line length with wordwrap (if used)*/ 
 
			while(len>0) 
			{ 
				ll = len; 
				npos = ll; 
				for(int i = 0; i < len; i++) 
				{ 
					if(s[i] == _T('\r') || s[i] == _T('\n')) 
					{ 
						if(s[i] == _T('\r') && ((i+1) < len) && s[i+1] == _T('\n')) 
							npos = i + 2; 
						else 
							npos = i + 1; 
 
						ll = i; 
						break; 
					} 
				} 
 
				if(!::GetTextExtentExPoint(dc, s , (ll > 512) ? 512 : ll, width, &rll, NULL, &sz) || sz.cy == 0) 
				{ 
					::GetTextExtentExPoint(dc, _T(" ") , 1, 0, NULL, NULL, &sz); 
					sz.cx = 0; 
					rll = ll; 
				} 
 
				if(rll>ll) 
					rll = ll; 
 
				if(!check_bits(dwStyle, HTC_WORDWRAP)) 
					rll = ll; 
				else 
					if(rll < ll) 
						npos = rll; 
 
				if(rll>0) 
				{ 
					if((rll < len) && !_istspace(s[rll])) 
						for(int i = rll - 1; i >= 0; i--) 
							if(_istspace(s[i])) 
							{ 
								rll = i; 
								npos = i + 1; 
								break; 
							} 
				} 
 
				if(npos == 0) 
					npos = 1; 
 
				CLineInfo li(pos, pos + rll - 1); 
				CLinePartInfo pl(pos, pos + rll - 1); 
 
				while(it != m_Text.GetLinks().end() && it->End() < pos) 
					it++; 
 
				while(it != m_Text.GetLinks().end() && (it->Begin() >= pl.Begin() && it->Begin() <= pl.End()) ||  
					(it->End() >= pl.Begin() && it->End() <= pl.End()) || 
					(pl.Begin()>=it->Begin() && pl.End() <= it->End())) 
				{ 
					int b = it->Begin(); 
					int e = it->End(); 
					if(bpl.End()) 
						e = pl.End(); 
 
					if(b>pl.Begin()) 
					{ 
						CLinePartInfo pln(pl.Begin(), b - 1); 
						li.push_back(pln); 
						pl.m_iBegin = e + 1; 
					} 
 
					CLinePartInfo pln(b, e, &*it); 
					li.push_back(pln); 
					pl.m_iBegin = e + 1;	 
 
					if(e < pl.End()) 
						it++; 
					else 
						break; 
				} 
 
				if(pl.Len()>0) 
					li.push_back(pl); 
 
				m_iLineHeight = sz.cy; 
				m_iLinesHeight+=m_iLineHeight; 
				if(sz.cx > m_iMaxWidth) 
					m_iMaxWidth = sz.cx; 
				if(iMaxWidthChars < li.Len()) 
					iMaxWidthChars = li.Len(); 
 
				m_Lines.push_back(li); 
 
				pos+=npos; 
				s+=npos; 
				len-=npos; 
 
				if(bFirstPhase && ((m_iLinesHeight + iScrollHeight) > rc.Height())) 
				{ 
					bFirstPhase = false; 
					ShowScrollBar(SB_VERT,TRUE); 
					dwStyle|=WS_VSCROLL; 
					goto second_phase; 
				} 
			} 
			if(bRepaint) 
				InvalidateRect(rc); 
		} 
 
		dc.SelectFont(hOldFont); 
		 
		// Update scroll bars 
		dwStyle = GetWindowLongPtr(GWL_STYLE); 
		if(check_bits(dwStyle, HTC_AUTO_SCROLL_BARS) && !check_bits(dwStyle, HTC_WORDWRAP)) 
		{ 
			if(m_iMaxWidth > rc.Width()) 
			{ 
				ShowScrollBar(SB_HORZ,TRUE); 
				dwStyle|=WS_HSCROLL; 
			}; 
		} 
 
		SCROLLINFO si; 
		si.cbSize = sizeof(si); 
		si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; 
 
		if(check_bits(dwStyle,WS_HSCROLL) && m_iMaxWidth != 0) 
		{ 
			si.nMin = 0; 
			si.nMax = iMaxWidthChars + iMaxWidthChars/2; 
			int i = m_Text.GetText().GetLength(); 
			si.nPos = (int)(double(si.nMax) * m_iHorzPos / 100); 
			si.nPage = (rc.Width() * si.nMax)/m_iMaxWidth; 
			SetScrollInfo(SB_HORZ, &si, FALSE); 
		} 
 
		if(check_bits(dwStyle,WS_VSCROLL) && m_iLinesHeight != 0) 
		{ 
			si.nMin = 0; 
			si.nMax = m_Lines.size(); 
			si.nPos = (int)(double(si.nMax) * m_iVertPos / 100); 
			si.nPage = (rc.Height() * si.nMax)/m_iLinesHeight; 
			SetScrollInfo(SB_VERT, &si, FALSE); 
		} 
 
		m_bDontUpdateSizeInfo = false; 
		UpdateVisLines(); 
	} 
 
	void UpdateFonts() 
	{ 
		DWORD dwStyle = GetWindowLongPtr(GWL_STYLE); 
 
		if(!m_LinksFont.IsNull()) 
			m_LinksFont.DeleteObject(); 
 
		if(!m_HoverFont.IsNull()) 
			m_HoverFont.DeleteObject(); 
 
		LOGFONT lf; 
		m_Font.GetLogFont(&lf); 
		if(check_bits(dwStyle, HTC_UNDERLINE_LINKS)) 
			lf.lfUnderline = TRUE; 
		m_LinksFont.CreateFontIndirect(&lf); 
 
		m_Font.GetLogFont(&lf); 
		if(check_bits(dwStyle, HTC_UNDERLINE_HOVER)) 
			lf.lfUnderline = TRUE; 
		m_HoverFont.CreateFontIndirect(&lf); 
	} 
 
	void UpdateVisLines() 
	{ 
		RestoreLink(); 
		DWORD dwStyle = GetWindowLongPtr(GWL_STYLE); 
 
		int id = 1; 
		if(check_bits(dwStyle, HTC_ENABLE_TOOLTIPS)) 
		{ 
			for(std::vector::iterator itv = m_VisLines.begin(); itv != m_VisLines.end(); itv++) 
				for(CVisLine::iterator jt = itv->begin(); jt != itv->end(); jt++) 
				{ 
					if(jt->m_pHyperLink != NULL) 
						m_tip.DelTool(*this, id++); 
				} 
		} 
 
		m_VisLines.clear(); 
 
		CClientDC dc(m_hWnd); // device context for painting 
 
		std::vector::iterator it = m_Lines.begin(); 
		int iVertPos = 0; 
		int iHorzPos = 0; 
		if(check_bits(dwStyle,WS_VSCROLL)) 
			iVertPos = GetScrollPos(SB_VERT); 
		if(check_bits(dwStyle,WS_HSCROLL)) 
			iHorzPos = GetScrollPos(SB_HORZ); 
 
		if(iVertPos >= (int)m_Lines.size()) 
			return; 
 
		it+=iVertPos; 
 
		CFontHandle hOldFont = dc.SelectFont(m_Font); 
		 
		int ypos = 0; 
		LPCTSTR s = m_Text.GetText(); 
 
		CRect rcClient; 
		GetClientRect(rcClient); 
 
		for(; it != m_Lines.end(); it++) 
		{ 
			int XPos = 2; 
			int LinePos = it->Begin(); 
			int Offset = 0; 
			int Len = 0; 
 
			CVisLine vl; 
			CRect rcBounds; 
 
			std::vector::iterator jt; 
 
			for(jt = it->begin(); jt != it->end(); jt++) 
			{ 
				if(jt->Begin() <= (LinePos + iHorzPos) && jt->End() >= (LinePos + iHorzPos)) 
				{ 
					Offset = LinePos + iHorzPos; 
					Len = jt->Len() - ((LinePos + iHorzPos) - jt->Begin()); 
					break; 
				} 
			} 
 
			while(jt != it->end()) 
			{ 
				if(Len > 0) 
				{ 
					SIZE sz; 
					::GetTextExtentExPoint(dc, s + Offset, Len, 0, NULL, NULL, &sz); 
 
					rcBounds.left = XPos; 
					XPos+=sz.cx; 
					rcBounds.right = XPos; 
					rcBounds.top = ypos; 
					rcBounds.bottom = ypos+m_iLineHeight; 
 
					vl.push_back(CVisPart(*jt, rcBounds, Offset, Len, NULL, NULL)); 
				} 
 
				if(XPos > rcClient.Width()) 
					break; 
 
				jt++; 
				Offset = jt->Begin(); 
				Len = jt->Len(); 
			} 
 
			m_VisLines.push_back(vl); 
			ypos+=m_iLineHeight; 
			if(ypos>rcClient.bottom) 
				break; 
		} 
 
		CVisPart *pPrev = NULL, *pNext; 
		 
		id = 1; 
		for(std::vector::iterator it2 = m_VisLines.begin(); it2 != m_VisLines.end(); it2++) 
			for(CVisLine::iterator jt = it2->begin(); jt != it2->end(); jt++) 
			{ 
				pNext = &*jt; 
				if(pPrev != NULL &&  
					pPrev->m_pHyperLink != NULL &&  
					pPrev->m_pHyperLink == pNext->m_pHyperLink && 
					pPrev != pNext) 
				{ 
					pPrev->m_pNext = pNext; 
					pNext->m_pPrev = pPrev; 
				} 
				pPrev = pNext; 
 
				if(check_bits(dwStyle, HTC_ENABLE_TOOLTIPS) && jt->m_pHyperLink != NULL) 
					m_tip.AddTool(*this, (LPCTSTR)jt->m_pHyperLink->Title(), jt->m_rcBounds, id++); 
			} 
 
		dc.SelectFont(hOldFont); 
	} 
 
	void HighlightLink(CVisPart* Part, const CPoint& MouseCoords) 
	{ 
		if(m_pActivePart == Part) 
			return; 
 
		if(m_pActivePart != Part && m_pActivePart != NULL && Part != NULL && m_pActivePart->m_pHyperLink != Part->m_pHyperLink) 
			RestoreLink(); 
 
		m_pActivePart = Part; 
		while(m_pActivePart->m_pPrev != NULL) 
			m_pActivePart = m_pActivePart->m_pPrev; 
 
		CClientDC dc(*this); 
		CFontHandle hOldFont = dc.SelectFont(m_HoverFont); 
		dc.SetBkColor(m_BkColor); 
		dc.SetTextColor(m_HoverColor); 
		LPCTSTR s = m_Text.GetText(); 
 
		CVisPart* p = m_pActivePart; 
		while(p != NULL) 
		{ 
			TextOut(dc, p->m_rcBounds.left, p->m_rcBounds.top,  
				s + p->m_iRealBegin, p->m_iRealLen); 
			p = p->m_pNext; 
		} 
 
		dc.SelectFont(hOldFont); 
 
		SetCursor(m_LinkCursor); 
	} 
 
	void RestoreLink() 
	{ 
		if(m_pActivePart == NULL) 
			return; 
 
		CClientDC dc(*this); 
		CFontHandle hOldFont = dc.SelectFont(m_LinksFont); 
		dc.SetBkColor(m_BkColor); 
		dc.SetTextColor(m_LinkColor); 
		LPCTSTR s = m_Text.GetText(); 
 
		CVisPart* p = m_pActivePart; 
		while(p != NULL) 
		{ 
			TextOut(dc, p->m_rcBounds.left, p->m_rcBounds.top,  
				s + p->m_iRealBegin, p->m_iRealLen); 
			p = p->m_pNext; 
		} 
 
		dc.SelectFont(hOldFont); 
 
		m_pActivePart = NULL; 
		SetCursor(m_DefaultCursor); 
	} 
}; 
 
// -------------------------------------------------------------- 
// CHyperTextCtrl 
 
class CHyperTextCtrl : public CHyperTextCtrlT< CWindowImpl > 
{ 
public: 
	//message map 
	BEGIN_MSG_MAP(CHyperTextCtrl) 
	MESSAGE_HANDLER(WM_PAINT, OnPaint) 
	MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage) 
	MESSAGE_HANDLER(WM_SIZE, OnSize) 
	MESSAGE_HANDLER(WM_SHOWWINDOW, OnShowWindow) 
	MESSAGE_HANDLER(WM_CREATE, OnCreate) 
	MESSAGE_HANDLER(WM_SETTEXT, OnSetText) 
	MESSAGE_HANDLER(WM_GETTEXT, OnGetText) 
	MESSAGE_HANDLER(WM_SETFONT, OnSetFont) 
	MESSAGE_HANDLER(WM_GETFONT, OnGetFont) 
	MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) 
	MESSAGE_HANDLER(WM_HSCROLL, OnHScroll) 
	MESSAGE_HANDLER(WM_VSCROLL, OnVScroll) 
	MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged) 
	REFLECT_NOTIFICATIONS() 
	END_MSG_MAP() 
 
	//window class 
	DECLARE_WND_CLASS_EX(_T("maqHyperTextCtrl"), CS_DBLCLKS | CS_HREDRAW, COLOR_WINDOW); 
}; 
 
class CHyperTextCtrlFSB : public CHyperTextCtrlT< CWindowImpl > 
{ 
public: 
	//message map 
	BEGIN_MSG_MAP(CHyperTextCtrl) 
	MESSAGE_HANDLER(WM_PAINT, OnPaint) 
	MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage) 
	MESSAGE_HANDLER(WM_SIZE, OnSize) 
	MESSAGE_HANDLER(WM_SHOWWINDOW, OnShowWindow) 
	MESSAGE_HANDLER(WM_CREATE, OnCreate) 
	MESSAGE_HANDLER(WM_SETTEXT, OnSetText) 
	MESSAGE_HANDLER(WM_GETTEXT, OnGetText) 
	MESSAGE_HANDLER(WM_SETFONT, OnSetFont) 
	MESSAGE_HANDLER(WM_GETFONT, OnGetFont) 
	MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) 
	MESSAGE_HANDLER(WM_HSCROLL, OnHScroll) 
	MESSAGE_HANDLER(WM_VSCROLL, OnVScroll) 
	MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged) 
	REFLECT_NOTIFICATIONS() 
	END_MSG_MAP() 
 
	//window class 
	DECLARE_WND_CLASS_EX(_T("maqHyperTextCtrlFSB"), CS_DBLCLKS | CS_HREDRAW, COLOR_WINDOW); 
 
	LRESULT OnCreate(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
	{ 
		FlatSB_Initialize(); 
		FlatSB_SetScrollProp(WSB_PROP_VSTYLE, FSB_FLAT_MODE, TRUE); 
		FlatSB_SetScrollProp(WSB_PROP_HSTYLE, FSB_FLAT_MODE, TRUE); 
		return CHyperTextCtrlT< CWindowImpl >::OnCreate(Msg, wParam, lParam, bHandled); 
	} 
}; 
 
// 
}; //namespace HyperTextControl 
 
#endif