// HyperTextCtrl.cpp: implementation of the CHyperTextCtrl class.
//
// Copyright (c) 2001 Magomed Abdurakhmanov
// maq@hotbox.ru, http://mickels.iwt.ru/en
//
//
//
// No warranties are given. Use at your own risk.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "HyperTextCtrl.h"
//////////////////////////////////////////////////////////////////////
// CHyperLink
namespace HyperTextControl{
inline bool check_bits(DWORD Value, DWORD Mask)
{
return (Value & Mask) == Mask;
}
CHyperLink::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::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::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;
}
CHyperLink::~CHyperLink()
{
;
}
void CHyperLink::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;
}
}
//////////////////////////////////////////////////////////////////////
// CPreparedHyperText
bool tspace(TCHAR c)
{
return _istspace(c) || c < _T(' ') || c == _T(';') || c == _T(',') || c == _T('!');
}
CPreparedHyperText::CPreparedHyperText()
{
}
CPreparedHyperText::CPreparedHyperText(const CString& sText)
{
PrepareText(sText);
}
CPreparedHyperText::CPreparedHyperText(const CPreparedHyperText& src)
{
m_sText = src.m_sText;
m_Links.assign(src.m_Links.begin(), src.m_Links.end());
}
CPreparedHyperText::~CPreparedHyperText()
{
;
}
void CPreparedHyperText::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);
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);
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);
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);
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);
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);
m_Links.push_back(CHyperLink(WordPos, WordPos + len - 1, s, s, (LPCTSTR)NULL));
state = space;
}
break;
}
}
m_Links.sort();
}
void CPreparedHyperText::Clear()
{
m_sText.Empty();
m_Links.erase(m_Links.begin(), m_Links.end());
}
void CPreparedHyperText::SetText(const CString& sText)
{
Clear();
PrepareText(sText);
}
void CPreparedHyperText::AppendText(const CString& sText)
{
int len = m_sText.GetLength();
CPreparedHyperText ht(sText);
m_sText+=sText;
for(std::list<CHyperLink>::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 CPreparedHyperText::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 CPreparedHyperText::AppendHyperLink(const CString& sText, const CString& sTitle, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
ATLASSERT(sText.GetLength()>0);
ATLASSERT(hWnd != 0);
int len = m_sText.GetLength();
m_sText+=sText;
m_Links.push_back(CHyperLink(len, len + sText.GetLength() - 1, sTitle, hWnd, uMsg, wParam, lParam));
}
//////////////////////////////////////////////////////////////////////
// CHyperTextCtrl
CHyperTextCtrl::CHyperTextCtrl()
{
}
CHyperTextCtrl::~CHyperTextCtrl()
{
}
BOOL CHyperTextCtrl::PreTranslateMessage(MSG* pMsg)
{
return FALSE;
}
LRESULT CHyperTextCtrl::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_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 CHyperTextCtrl::OnPaint(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
CPaintDC dc(m_hWnd); // device context for painting
CFontHandle hOldFont = dc.SelectFont(m_Font);
COLORREF clText = GetSysColor(COLOR_WINDOWTEXT);
COLORREF clWindow = GetSysColor(COLOR_WINDOW);
dc.SetBkColor(clWindow);
int ypos = 0;
LPCTSTR s = m_Text.m_sText;
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;
dc.FillRect(rc, (HBRUSH) (COLOR_WINDOW+1));
for(std::vector<CVisLine>::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(clText);
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, (HBRUSH) (COLOR_WINDOW+1));
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, (HBRUSH) (COLOR_WINDOW+1));
dc.SelectFont(hOldFont);
return 0;
}
LRESULT CHyperTextCtrl::OnSize(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
WORD cx, cy;
cx = LOWORD(lParam);
cy = HIWORD(lParam);
UpdateSize(true);
return 0;
}
LRESULT CHyperTextCtrl::OnSetText(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
m_Text.PrepareText((LPTSTR)lParam);
UpdateSize(true);
return TRUE;
}
LRESULT CHyperTextCtrl::OnGetText(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
int bufsize = wParam;
LPTSTR buf = (LPTSTR)lParam;
if(lParam == NULL || bufsize == 0 || m_Text.m_sText.IsEmpty())
return 0;
int cpy = m_Text.m_sText.GetLength() > (bufsize-1) ? (bufsize-1) : m_Text.m_sText.GetLength();
_tcsncpy(buf, m_Text.m_sText, cpy);
return cpy;
}
LRESULT CHyperTextCtrl::OnSetFont(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
m_Font = (HFONT)wParam;
UpdateFonts();
UpdateSize(LOWORD(lParam) != 0);
return 0;
}
LRESULT CHyperTextCtrl::OnGetFont(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
return (LRESULT)m_Font.m_hFont;
}
LRESULT CHyperTextCtrl::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 CHyperTextCtrl::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 CHyperTextCtrl::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();
int i = pt.y / m_iLineHeight;
if(i < m_VisLines.size())
{
std::vector<CVisLine>::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;
int i = pt.y / m_iLineHeight;
if(i < m_VisLines.size())
{
std::vector<CVisLine>::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();
break;
}
}
}
}
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<iScrollLines; i++)
PostMessage(WM_VSCROLL, SB_LINEDOWN, 0);
}
else
{
for(int i = 0; i<iScrollLines; i++)
PostMessage(WM_VSCROLL, SB_LINEUP, 0);
}
m_iWheelDelta %= WHEEL_DELTA;
}
}
}
MSG msg = { m_hWnd, Msg, wParam, lParam };
if(m_tip.IsWindow())
m_tip.RelayEvent(&msg);
return 0;
}
LRESULT CHyperTextCtrl::OnCaptureChanged(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
RestoreLink();
return 0;
}
void CHyperTextCtrl::UpdateSize(bool bRepaint/* = false*/)
{
if(m_bDontUpdateSizeInfo)
return;
m_bDontUpdateSizeInfo = true;
DWORD dwStyle = GetWindowLongPtr(GWL_STYLE);
bool bFirstPhase = false;
if(check_bits(dwStyle, HTC_AUTO_SCROLL_BARS))
{
ShowScrollBar(SB_BOTH, FALSE);
bFirstPhase = true;
}
CClientDC dc(*this);
CFontHandle hOldFont = dc.SelectFont(m_Font);
int iScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
second_phase:
m_Lines.clear();
CRect rc;
GetClientRect(rc);
rc.DeflateRect(2,0);
m_iMaxWidth = 0;
m_iLinesHeight = 0;
long iMaxWidthChars = 0;
SIZE sz;
if(rc.Width() > 5 && rc.Height() > 5)
{
std::list<CHyperLink>::iterator it = m_Text.m_Links.begin();
LPCTSTR s = m_Text.m_sText;
int len = m_Text.m_sText.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))
{
::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;
}
}
CLineInfo li(pos, pos + rll - 1);
CLinePartInfo pl(pos, pos + rll - 1);
while(it != m_Text.m_Links.end() && it->End() < pos)
it++;
while(it != m_Text.m_Links.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(b<pl.Begin())
b = pl.Begin();
if(e>pl.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))
{
si.nMin = 0;
si.nMax = iMaxWidthChars + iMaxWidthChars/2;
int i = m_Text.m_sText.GetLength();
si.nPos = 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))
{
si.nMin = 0;
si.nMax = m_Lines.size();
si.nPos = double(si.nMax) * m_iVertPos / 100;
si.nPage = (rc.Height() * si.nMax)/m_iLinesHeight;
SetScrollInfo(SB_VERT, &si, FALSE);
}
m_bDontUpdateSizeInfo = false;
UpdateVisLines();
}
void CHyperTextCtrl::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 CHyperTextCtrl::UpdateVisLines()
{
RestoreLink();
DWORD dwStyle = GetWindowLongPtr(GWL_STYLE);
int id = 1;
if(check_bits(dwStyle, HTC_ENABLE_TOOLTIPS))
{
for(std::vector<CVisLine>::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<CLineInfo>::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 >= m_Lines.size())
return;
it+=iVertPos;
CFontHandle hOldFont = dc.SelectFont(m_Font);
int ypos = 0;
LPCTSTR s = m_Text.m_sText;
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<CLinePartInfo>::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<CVisLine>::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 CHyperTextCtrl::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);
COLORREF clWindow = GetSysColor(COLOR_WINDOW);
dc.SetBkColor(clWindow);
dc.SetTextColor(m_HoverColor);
LPCTSTR s = m_Text.m_sText;
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 CHyperTextCtrl::RestoreLink()
{
if(m_pActivePart == NULL)
return;
CClientDC dc(*this);
CFontHandle hOldFont = dc.SelectFont(m_LinksFont);
COLORREF clWindow = GetSysColor(COLOR_WINDOW);
dc.SetBkColor(clWindow);
dc.SetTextColor(m_LinkColor);
LPCTSTR s = m_Text.m_sText;
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);
}
LRESULT CHyperTextCtrl::OnEraseBkgnd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
return TRUE;
}
//
}; //namespace HyperTextControl