--- /dev/null
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2006,2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "StdAfx.h"\r
+#include "htmlformatter.h"\r
+\r
+CHTMLFormatter::CHTMLFormatter(void)\r
+{\r
+}\r
+\r
+CHTMLFormatter::~CHTMLFormatter(void)\r
+{\r
+}\r
+\r
+CSize CHTMLFormatter::DrawHTML(CDC * pDC, CRect rect, CString str, LOGFONT font, BOOL bCalculate /* = FALSE */)\r
+{\r
+ CUIntArray nLengthLines;\r
+ int nLine = 0;\r
+ int nCmd = NONE;\r
+ STATEMACHINE nState = BEGIN_TAG;\r
+ int nAlign = ALIGN_LEFT;\r
+ BOOL bCloseTag = FALSE;\r
+\r
+ m_arLinkRects.RemoveAll();\r
+ m_arLinkURLs.RemoveAll();\r
+\r
+ CSize sz(0, 0);\r
+\r
+ if (str.IsEmpty())\r
+ return sz;\r
+\r
+ CPoint pt = rect.TopLeft();\r
+ CPoint ptCur = pt;\r
+ \r
+\r
+ COLORREF crText = pDC->GetTextColor();\r
+ COLORREF crBg = pDC->GetBkColor();\r
+\r
+ LOGFONT lf;\r
+ memcpy(&lf, &font, sizeof(LOGFONT));\r
+\r
+ CFont tempFont;\r
+ tempFont.CreateFontIndirect(&lf);\r
+\r
+ CFont * pOldFont = pDC->SelectObject(&tempFont);\r
+\r
+ TEXTMETRIC textMetric;\r
+ pDC->GetTextMetrics(&textMetric);\r
+ int nHeight = textMetric.tmHeight;\r
+ int nWidth = textMetric.tmAveCharWidth;\r
+\r
+ CString strTag = _T("");\r
+ CString strText = _T("");\r
+ UINT nParam = 0;\r
+ \r
+ CRect linkRect;\r
+ linkRect.SetRectEmpty();\r
+\r
+ CUIntArray percent;\r
+ percent.Add(0);\r
+ \r
+ int nTemp = 0; //the temporary variable\r
+ BOOL bFirstOutput = TRUE;\r
+\r
+ //iterate through all characters of the string\r
+ for (int i = 0; i <= str.GetLength(); i++)\r
+ {\r
+ if (i < str.GetLength())\r
+ {\r
+ //Searches the command and parameters in the string\r
+ switch (nState)\r
+ {\r
+ case BEGIN_TAG:\r
+ //waiting for the begin of a tag (<tag>), newline ('\n' or '\r') or tab ('\t')\r
+ switch (str.GetAt(i))\r
+ {\r
+ case _T('<'):\r
+ nState = TEXT_TAG; //statemachine to 'waiting for the tag'\r
+ bCloseTag = FALSE; //opening bracket\r
+ strTag = _T("");\r
+ break;\r
+ case _T('\n'):\r
+ nCmd = NEW_LINE;\r
+ nParam = 1;\r
+ break;\r
+ case _T('\t'):\r
+ nCmd = TABULATION;\r
+ nParam = 1;\r
+ break;\r
+ case _T('\r'):\r
+ break;\r
+ default: \r
+ strText += str.GetAt(i);\r
+ break;\r
+ }\r
+ break;\r
+ case TEXT_TAG:\r
+ //get the tag itself (until the closing bracket ('>'))\r
+ switch (str.GetAt(i))\r
+ {\r
+ case _T('/'):\r
+ if (strTag.IsEmpty())\r
+ bCloseTag = TRUE; //found the char's cancel tag\r
+ break;\r
+ case _T('<'):\r
+ if (strTag.IsEmpty())\r
+ {\r
+ nState = BEGIN_TAG;\r
+ strText += str.GetAt(i);\r
+ }\r
+ else strTag += str.GetAt(i);\r
+ break;\r
+ case _T('='):\r
+ case _T('>'):\r
+ i--;\r
+ //case _T(' '):\r
+ //Analyses tags\r
+ if (strTag.CompareNoCase(_T("b"))==0)\r
+ {\r
+ //Bold text\r
+ nCmd = BOLD;\r
+ nState = END_TAG;\r
+ }\r
+ else if (strTag.CompareNoCase(_T("i")) == 0)\r
+ {\r
+ //Italic text\r
+ nCmd = ITALIC;\r
+ nState = END_TAG;\r
+ }\r
+ else if (strTag.CompareNoCase(_T("s")) == 0)\r
+ {\r
+ //Strikeout text\r
+ nCmd = STRIKE;\r
+ nState = END_TAG;\r
+ }\r
+ else if (strTag.CompareNoCase(_T("u")) == 0)\r
+ {\r
+ //Underline text\r
+ nCmd = UNDERLINE;\r
+ nState = END_TAG;\r
+ }\r
+ else if (strTag.CompareNoCase(_T("t")) == 0)\r
+ {\r
+ //Tabulation\r
+ nCmd = TABULATION;\r
+ nParam = 1;\r
+ nState = BEGIN_NUMBER;\r
+ }\r
+ else if (strTag.CompareNoCase(_T("ct")) == 0)\r
+ {\r
+ //Color of the text\r
+ nCmd = COLOR_TEXT;\r
+ nParam = crText;\r
+ nState = BEGIN_NUMBER;\r
+ }\r
+ else if (strTag.CompareNoCase(_T("cb")) == 0)\r
+ {\r
+ //Color of the background\r
+ nCmd = COLOR_BK;\r
+ nParam = crBg;\r
+ nState = BEGIN_NUMBER;\r
+ }\r
+ else if (strTag.CompareNoCase(_T("al")) == 0)\r
+ {\r
+ //left align\r
+ nAlign = ALIGN_LEFT;\r
+ nState = END_TAG;\r
+ }\r
+ else if (strTag.CompareNoCase(_T("ac")) == 0)\r
+ {\r
+ //center align\r
+ if (!bCalculate)\r
+ nAlign = bCloseTag ? ALIGN_LEFT : ALIGN_CENTER;\r
+ nState = END_TAG;\r
+ }\r
+ else if (strTag.CompareNoCase(_T("ar")) == 0)\r
+ {\r
+ //right align\r
+ if (!bCalculate)\r
+ nAlign = bCloseTag ? ALIGN_LEFT : ALIGN_RIGHT;\r
+ nState = END_TAG;\r
+ }\r
+ else if (strTag.CompareNoCase(_T("hr")) == 0)\r
+ {\r
+ //horizontal line\r
+ nCmd = HORZ_LINE_PERCENT;\r
+ nParam = 100;\r
+ nState = BEGIN_NUMBER;\r
+ }\r
+ else if (strTag.CompareNoCase(_T("a")) == 0)\r
+ {\r
+ //link\r
+ nCmd = LINK;\r
+ nState = BEGIN_URL; //wait for '='\r
+ }\r
+ else nState = END_TAG; //Unknown tag\r
+ break;\r
+ default:\r
+ strTag += str.GetAt(i);\r
+ break;\r
+ }\r
+ break;\r
+ case END_TAG:\r
+ //waiting for the end of the tag\r
+ if (str.GetAt(i) == _T('>'))\r
+ nState = BEGIN_TAG;\r
+ break;\r
+ case BEGIN_NUMBER:\r
+ //waiting for the start of a number\r
+ if (str.GetAt(i) == _T('='))\r
+ {\r
+ strTag = _T("");\r
+ nState = TEXT_NUMBER;\r
+ }\r
+ else if (str.GetAt(i) == _T('>'))\r
+ nState = BEGIN_TAG; //not a number\r
+ break;\r
+ case BEGIN_URL:\r
+ //waiting for the start of a number\r
+ if (str.GetAt(i) == _T('='))\r
+ {\r
+ strTag = _T("");\r
+ nState = TEXT_URL;\r
+ }\r
+ else if (str.GetAt(i) == _T('>'))\r
+ nState = BEGIN_TAG; //not a url\r
+ break;\r
+ case TEXT_NUMBER:\r
+ //waiting for a number string\r
+ switch (str.GetAt(i))\r
+ {\r
+ case _T('>'):\r
+ i --;\r
+ //intended fall through!\r
+ case _T('%'):\r
+ //Gets the real number from the string\r
+ if (!strTag.IsEmpty())\r
+ nParam = _tcstoul(strTag, 0, 0);\r
+ nState = END_TAG;\r
+ break;\r
+ default:\r
+ strTag += str.GetAt(i);\r
+ break;\r
+ }\r
+ break;\r
+ case TEXT_URL:\r
+ //waiting for a url\r
+ switch (str.GetAt(i))\r
+ {\r
+ case _T('>'):\r
+ i--;\r
+ if (!strTag.IsEmpty())\r
+ m_arLinkURLs.Add(strTag);\r
+ nState = END_TAG;\r
+ break;\r
+ default:\r
+ strTag += str.GetAt(i);\r
+ break;\r
+ }\r
+ } // switch (nState)\r
+ }\r
+ else\r
+ {\r
+ //Immitates new line at the end of the string\r
+ nState = BEGIN_TAG;\r
+ nCmd = NEW_LINE;\r
+ nParam = 1;\r
+ }\r
+\r
+ if ((nState == BEGIN_TAG) && (nCmd != NONE))\r
+ {\r
+ //New Command with full parameters\r
+ if (!strText.IsEmpty())\r
+ {\r
+ if (bFirstOutput)\r
+ {\r
+ switch (nAlign)\r
+ {\r
+ case ALIGN_CENTER:\r
+ ptCur.x = pt.x + (rect.Width() - nLengthLines.GetAt(nLine)) / 2;\r
+ break;\r
+ case ALIGN_RIGHT:\r
+ ptCur.x = pt.x + rect.Width() - nLengthLines.GetAt(nLine);\r
+ break;\r
+ }\r
+ }\r
+ if (!bCalculate)\r
+ pDC->TextOut(ptCur.x, ptCur.y, strText);\r
+ CSize s = pDC->GetTextExtent(strText);\r
+ linkRect.left = ptCur.x;\r
+ linkRect.top = ptCur.y;\r
+ linkRect.right = linkRect.left + s.cx;\r
+ linkRect.bottom = linkRect.top + s.cy;\r
+ ptCur.x += s.cx;\r
+ strText = _T("");\r
+ bFirstOutput = FALSE;\r
+ }\r
+ \r
+ //Executes command\r
+ switch (nCmd)\r
+ {\r
+ case LINK:\r
+ if (bCloseTag)\r
+ {\r
+ //closing the link\r
+ m_arLinkRects.Add(linkRect);\r
+ linkRect.SetRectEmpty();\r
+ }\r
+ break;\r
+ case BOLD:\r
+ //Bold text\r
+ pDC->SelectObject(pOldFont);\r
+ tempFont.DeleteObject();\r
+ lf.lfWeight = font.lfWeight;\r
+ if (!bCloseTag)\r
+ {\r
+ lf.lfWeight *= 2;\r
+ if (lf.lfWeight > FW_BLACK)\r
+ lf.lfWeight = FW_BLACK;\r
+ }\r
+ tempFont.CreateFontIndirect(&lf);\r
+ pDC->SelectObject(&tempFont);\r
+ break;\r
+ case ITALIC:\r
+ //Italic text\r
+ pDC->SelectObject(pOldFont);\r
+ tempFont.DeleteObject();\r
+ lf.lfItalic = bCloseTag ? FALSE : TRUE;\r
+ tempFont.CreateFontIndirect(&lf);\r
+ pDC->SelectObject(&tempFont);\r
+ break;\r
+ case STRIKE:\r
+ //Strikeout text\r
+ pDC->SelectObject(pOldFont);\r
+ tempFont.DeleteObject();\r
+ lf.lfStrikeOut = bCloseTag ? FALSE : TRUE;\r
+ tempFont.CreateFontIndirect(&lf);\r
+ pDC->SelectObject(&tempFont);\r
+ break;\r
+ case UNDERLINE:\r
+ //Underline text\r
+ pDC->SelectObject(pOldFont);\r
+ tempFont.DeleteObject();\r
+ lf.lfUnderline = bCloseTag ? FALSE : TRUE;\r
+ tempFont.CreateFontIndirect(&lf);\r
+ pDC->SelectObject(&tempFont);\r
+ break;\r
+ case COLOR_TEXT:\r
+ //Color of the text\r
+ pDC->SetTextColor((COLORREF)nParam);\r
+ break;\r
+ case COLOR_BK:\r
+ //Color of the background\r
+ pDC->SetBkColor((COLORREF)nParam);\r
+ pDC->SetBkMode(bCloseTag ? TRANSPARENT : OPAQUE);\r
+ break;\r
+ case HORZ_LINE_PERCENT:\r
+ //Horizontal line with percent length\r
+ if (bCalculate)\r
+ {\r
+ percent.SetAt(nLine, percent.GetAt(nLine) + nParam);\r
+ nParam = 0;\r
+ }\r
+ else nParam = ::MulDiv(rect.Width(), nParam, 100);\r
+ case HORZ_LINE:\r
+ //Horizontal line with absolute length\r
+ //If text to output is exist\r
+ if (bFirstOutput)\r
+ {\r
+ switch (nAlign)\r
+ {\r
+ case ALIGN_CENTER:\r
+ ptCur.x = pt.x + (rect.Width() - nLengthLines.GetAt(nLine)) / 2;\r
+ break;\r
+ case ALIGN_RIGHT:\r
+ ptCur.x = pt.x + rect.Width() - nLengthLines.GetAt(nLine);\r
+ break;\r
+ }\r
+ }\r
+ DrawHorzLine(pDC, ptCur.x, ptCur.x + nParam, ptCur.y + nHeight / 2);\r
+ ptCur.x += nParam;\r
+ bFirstOutput = FALSE;\r
+ break;\r
+ case NEW_LINE:\r
+ //New line\r
+ if (!nParam)\r
+ nParam = 1;\r
+ sz.cx = max(sz.cx, ptCur.x - pt.x);\r
+ nLengthLines.Add(ptCur.x - pt.x); //Adds the real length of the lines\r
+ ptCur.y += nHeight * nParam;\r
+ nLine ++;\r
+ percent.Add(0);\r
+ bFirstOutput = TRUE;\r
+ ptCur.x = pt.x;\r
+ break;\r
+ case TABULATION:\r
+ //Tabulation\r
+ if (!nParam)\r
+ nParam = 1;\r
+ nTemp = (ptCur.x - pt.x) % (nWidth * 4);\r
+ if (nTemp)\r
+ {\r
+ //aligns with tab\r
+ ptCur.x += (nWidth * 4) - nTemp;\r
+ nParam --;\r
+ }\r
+ ptCur.x += (nParam * nWidth * 4);\r
+ break;\r
+ }\r
+ //Resets the last command\r
+ nCmd = NONE;\r
+ bCloseTag = FALSE;\r
+ }\r
+ }\r
+\r
+ //Gets real height of the tooltip\r
+ sz.cy = ptCur.y - pt.y;\r
+\r
+ pDC->SelectObject(pOldFont);\r
+ tempFont.DeleteObject();\r
+\r
+ //Adds the percent's length to the line's length\r
+ for (int i = 0; i < percent.GetSize(); i++)\r
+ {\r
+ if (percent.GetAt(i))\r
+ nLengthLines.SetAt(i, nLengthLines.GetAt(i) + ::MulDiv(percent.GetAt(i), sz.cx, 100));\r
+ }\r
+\r
+ return sz;\r
+}\r
+\r
+void CHTMLFormatter::DrawHorzLine(CDC * pDC, int xStart, int xEnd, int y)\r
+{\r
+ CPen pen(PS_SOLID, 1, pDC->GetTextColor());\r
+ CPen * penOld = pDC->SelectObject(&pen);\r
+ pDC->MoveTo(xStart, y);\r
+ pDC->LineTo(xEnd, y);\r
+ pDC->SelectObject(penOld);\r
+ pen.DeleteObject();\r
+}\r
+\r
+BOOL CHTMLFormatter::IsPointOverALink(CPoint pt)\r
+{\r
+ CRect rect;\r
+ for (int i=0; i<m_arLinkRects.GetCount(); i++)\r
+ {\r
+ rect = m_arLinkRects.GetAt(i);\r
+ if (rect.PtInRect(pt))\r
+ return TRUE;\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+CString CHTMLFormatter::GetLinkForPoint(CPoint pt)\r
+{\r
+ CRect rect;\r
+ for (int i=0; i<m_arLinkRects.GetCount(); i++)\r
+ {\r
+ rect = m_arLinkRects.GetAt(i);\r
+ if (rect.PtInRect(pt))\r
+ {\r
+ return m_arLinkURLs.GetAt(i);\r
+ }\r
+ }\r
+ return _T("");\r
+}\r