No need to explicitly initialize CString and use Empty() for clearing CString
[TortoiseGit.git] / src / Utils / MiscUI / HTMLFormatter.cpp
blob77bd2e3e5ca216044ebf09b82b97fffa3da411eb
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2006,2008 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "stdafx.h"
20 #include "htmlformatter.h"
22 CHTMLFormatter::CHTMLFormatter(void)
26 CHTMLFormatter::~CHTMLFormatter(void)
30 CSize CHTMLFormatter::DrawHTML(CDC * pDC, CRect rect, CString str, LOGFONT font, BOOL bCalculate /* = FALSE */)
32 CUIntArray nLengthLines;
33 int nLine = 0;
34 int nCmd = NONE;
35 STATEMACHINE nState = BEGIN_TAG;
36 int nAlign = ALIGN_LEFT;
37 BOOL bCloseTag = FALSE;
39 m_arLinkRects.RemoveAll();
40 m_arLinkURLs.RemoveAll();
42 CSize sz(0, 0);
44 if (str.IsEmpty())
45 return sz;
47 CPoint pt = rect.TopLeft();
48 CPoint ptCur = pt;
51 COLORREF crText = pDC->GetTextColor();
52 COLORREF crBg = pDC->GetBkColor();
54 LOGFONT lf;
55 memcpy(&lf, &font, sizeof(LOGFONT));
57 CFont tempFont;
58 tempFont.CreateFontIndirect(&lf);
60 CFont * pOldFont = pDC->SelectObject(&tempFont);
62 TEXTMETRIC textMetric;
63 pDC->GetTextMetrics(&textMetric);
64 int nHeight = textMetric.tmHeight;
65 int nWidth = textMetric.tmAveCharWidth;
67 CString strTag;
68 CString strText;
69 UINT nParam = 0;
71 CRect linkRect;
72 linkRect.SetRectEmpty();
74 CUIntArray percent;
75 percent.Add(0);
77 int nTemp = 0; //the temporary variable
78 BOOL bFirstOutput = TRUE;
80 //iterate through all characters of the string
81 for (int i = 0; i <= str.GetLength(); i++)
83 if (i < str.GetLength())
85 //Searches the command and parameters in the string
86 switch (nState)
88 case BEGIN_TAG:
89 //waiting for the begin of a tag (<tag>), newline ('\n' or '\r') or tab ('\t')
90 switch (str.GetAt(i))
92 case _T('<'):
93 nState = TEXT_TAG; //statemachine to 'waiting for the tag'
94 bCloseTag = FALSE; //opening bracket
95 strTag.Empty();
96 break;
97 case _T('\n'):
98 nCmd = NEW_LINE;
99 nParam = 1;
100 break;
101 case _T('\t'):
102 nCmd = TABULATION;
103 nParam = 1;
104 break;
105 case _T('\r'):
106 break;
107 default:
108 strText += str.GetAt(i);
109 break;
111 break;
112 case TEXT_TAG:
113 //get the tag itself (until the closing bracket ('>'))
114 switch (str.GetAt(i))
116 case _T('/'):
117 if (strTag.IsEmpty())
118 bCloseTag = TRUE; //found the char's cancel tag
119 break;
120 case _T('<'):
121 if (strTag.IsEmpty())
123 nState = BEGIN_TAG;
124 strText += str.GetAt(i);
126 else strTag += str.GetAt(i);
127 break;
128 case _T('='):
129 case _T('>'):
130 i--;
131 //case _T(' '):
132 //Analyses tags
133 if (strTag.CompareNoCase(_T("b"))==0)
135 //Bold text
136 nCmd = BOLD;
137 nState = END_TAG;
139 else if (strTag.CompareNoCase(_T("i")) == 0)
141 //Italic text
142 nCmd = ITALIC;
143 nState = END_TAG;
145 else if (strTag.CompareNoCase(_T("s")) == 0)
147 //Strikeout text
148 nCmd = STRIKE;
149 nState = END_TAG;
151 else if (strTag.CompareNoCase(_T("u")) == 0)
153 //Underline text
154 nCmd = UNDERLINE;
155 nState = END_TAG;
157 else if (strTag.CompareNoCase(_T("t")) == 0)
159 //Tabulation
160 nCmd = TABULATION;
161 nParam = 1;
162 nState = BEGIN_NUMBER;
164 else if (strTag.CompareNoCase(_T("ct")) == 0)
166 //Color of the text
167 nCmd = COLOR_TEXT;
168 nParam = crText;
169 nState = BEGIN_NUMBER;
171 else if (strTag.CompareNoCase(_T("cb")) == 0)
173 //Color of the background
174 nCmd = COLOR_BK;
175 nParam = crBg;
176 nState = BEGIN_NUMBER;
178 else if (strTag.CompareNoCase(_T("al")) == 0)
180 //left align
181 nAlign = ALIGN_LEFT;
182 nState = END_TAG;
184 else if (strTag.CompareNoCase(_T("ac")) == 0)
186 //center align
187 if (!bCalculate)
188 nAlign = bCloseTag ? ALIGN_LEFT : ALIGN_CENTER;
189 nState = END_TAG;
191 else if (strTag.CompareNoCase(_T("ar")) == 0)
193 //right align
194 if (!bCalculate)
195 nAlign = bCloseTag ? ALIGN_LEFT : ALIGN_RIGHT;
196 nState = END_TAG;
198 else if (strTag.CompareNoCase(_T("hr")) == 0)
200 //horizontal line
201 nCmd = HORZ_LINE_PERCENT;
202 nParam = 100;
203 nState = BEGIN_NUMBER;
205 else if (strTag.CompareNoCase(_T("a")) == 0)
207 //link
208 nCmd = LINK;
209 nState = BEGIN_URL; //wait for '='
211 else nState = END_TAG; //Unknown tag
212 break;
213 default:
214 strTag += str.GetAt(i);
215 break;
217 break;
218 case END_TAG:
219 //waiting for the end of the tag
220 if (str.GetAt(i) == _T('>'))
221 nState = BEGIN_TAG;
222 break;
223 case BEGIN_NUMBER:
224 //waiting for the start of a number
225 if (str.GetAt(i) == _T('='))
227 strTag.Empty();
228 nState = TEXT_NUMBER;
230 else if (str.GetAt(i) == _T('>'))
231 nState = BEGIN_TAG; //not a number
232 break;
233 case BEGIN_URL:
234 //waiting for the start of a number
235 if (str.GetAt(i) == _T('='))
237 strTag.Empty();
238 nState = TEXT_URL;
240 else if (str.GetAt(i) == _T('>'))
241 nState = BEGIN_TAG; //not a url
242 break;
243 case TEXT_NUMBER:
244 //waiting for a number string
245 switch (str.GetAt(i))
247 case _T('>'):
248 i --;
249 //intended fall through!
250 case _T('%'):
251 //Gets the real number from the string
252 if (!strTag.IsEmpty())
253 nParam = _tcstoul(strTag, 0, 0);
254 nState = END_TAG;
255 break;
256 default:
257 strTag += str.GetAt(i);
258 break;
260 break;
261 case TEXT_URL:
262 //waiting for a url
263 switch (str.GetAt(i))
265 case _T('>'):
266 i--;
267 if (!strTag.IsEmpty())
268 m_arLinkURLs.Add(strTag);
269 nState = END_TAG;
270 break;
271 default:
272 strTag += str.GetAt(i);
273 break;
275 } // switch (nState)
277 else
279 //Immitates new line at the end of the string
280 nState = BEGIN_TAG;
281 nCmd = NEW_LINE;
282 nParam = 1;
285 if ((nState == BEGIN_TAG) && (nCmd != NONE))
287 //New Command with full parameters
288 if (!strText.IsEmpty())
290 if (bFirstOutput)
292 switch (nAlign)
294 case ALIGN_CENTER:
295 ptCur.x = pt.x + (rect.Width() - nLengthLines.GetAt(nLine)) / 2;
296 break;
297 case ALIGN_RIGHT:
298 ptCur.x = pt.x + rect.Width() - nLengthLines.GetAt(nLine);
299 break;
302 if (!bCalculate)
303 pDC->TextOut(ptCur.x, ptCur.y, strText);
304 CSize s = pDC->GetTextExtent(strText);
305 linkRect.left = ptCur.x;
306 linkRect.top = ptCur.y;
307 linkRect.right = linkRect.left + s.cx;
308 linkRect.bottom = linkRect.top + s.cy;
309 ptCur.x += s.cx;
310 strText.Empty();
311 bFirstOutput = FALSE;
314 //Executes command
315 switch (nCmd)
317 case LINK:
318 if (bCloseTag)
320 //closing the link
321 m_arLinkRects.Add(linkRect);
322 linkRect.SetRectEmpty();
324 break;
325 case BOLD:
326 //Bold text
327 pDC->SelectObject(pOldFont);
328 tempFont.DeleteObject();
329 lf.lfWeight = font.lfWeight;
330 if (!bCloseTag)
332 lf.lfWeight *= 2;
333 if (lf.lfWeight > FW_BLACK)
334 lf.lfWeight = FW_BLACK;
336 tempFont.CreateFontIndirect(&lf);
337 pDC->SelectObject(&tempFont);
338 break;
339 case ITALIC:
340 //Italic text
341 pDC->SelectObject(pOldFont);
342 tempFont.DeleteObject();
343 lf.lfItalic = bCloseTag ? FALSE : TRUE;
344 tempFont.CreateFontIndirect(&lf);
345 pDC->SelectObject(&tempFont);
346 break;
347 case STRIKE:
348 //Strikeout text
349 pDC->SelectObject(pOldFont);
350 tempFont.DeleteObject();
351 lf.lfStrikeOut = bCloseTag ? FALSE : TRUE;
352 tempFont.CreateFontIndirect(&lf);
353 pDC->SelectObject(&tempFont);
354 break;
355 case UNDERLINE:
356 //Underline text
357 pDC->SelectObject(pOldFont);
358 tempFont.DeleteObject();
359 lf.lfUnderline = bCloseTag ? FALSE : TRUE;
360 tempFont.CreateFontIndirect(&lf);
361 pDC->SelectObject(&tempFont);
362 break;
363 case COLOR_TEXT:
364 //Color of the text
365 pDC->SetTextColor((COLORREF)nParam);
366 break;
367 case COLOR_BK:
368 //Color of the background
369 pDC->SetBkColor((COLORREF)nParam);
370 pDC->SetBkMode(bCloseTag ? TRANSPARENT : OPAQUE);
371 break;
372 case HORZ_LINE_PERCENT:
373 //Horizontal line with percent length
374 if (bCalculate)
376 percent.SetAt(nLine, percent.GetAt(nLine) + nParam);
377 nParam = 0;
379 else nParam = ::MulDiv(rect.Width(), nParam, 100);
380 case HORZ_LINE:
381 //Horizontal line with absolute length
382 //If text to output is exist
383 if (bFirstOutput)
385 switch (nAlign)
387 case ALIGN_CENTER:
388 ptCur.x = pt.x + (rect.Width() - nLengthLines.GetAt(nLine)) / 2;
389 break;
390 case ALIGN_RIGHT:
391 ptCur.x = pt.x + rect.Width() - nLengthLines.GetAt(nLine);
392 break;
395 DrawHorzLine(pDC, ptCur.x, ptCur.x + nParam, ptCur.y + nHeight / 2);
396 ptCur.x += nParam;
397 bFirstOutput = FALSE;
398 break;
399 case NEW_LINE:
400 //New line
401 if (!nParam)
402 nParam = 1;
403 sz.cx = max(sz.cx, ptCur.x - pt.x);
404 nLengthLines.Add(ptCur.x - pt.x); //Adds the real length of the lines
405 ptCur.y += nHeight * nParam;
406 nLine ++;
407 percent.Add(0);
408 bFirstOutput = TRUE;
409 ptCur.x = pt.x;
410 break;
411 case TABULATION:
412 //Tabulation
413 if (!nParam)
414 nParam = 1;
415 nTemp = (ptCur.x - pt.x) % (nWidth * 4);
416 if (nTemp)
418 //aligns with tab
419 ptCur.x += (nWidth * 4) - nTemp;
420 nParam --;
422 ptCur.x += (nParam * nWidth * 4);
423 break;
425 //Resets the last command
426 nCmd = NONE;
427 bCloseTag = FALSE;
431 //Gets real height of the tooltip
432 sz.cy = ptCur.y - pt.y;
434 pDC->SelectObject(pOldFont);
435 tempFont.DeleteObject();
437 //Adds the percent's length to the line's length
438 for (int i = 0; i < percent.GetSize(); i++)
440 if (percent.GetAt(i))
441 nLengthLines.SetAt(i, nLengthLines.GetAt(i) + ::MulDiv(percent.GetAt(i), sz.cx, 100));
444 return sz;
447 void CHTMLFormatter::DrawHorzLine(CDC * pDC, int xStart, int xEnd, int y)
449 CPen pen(PS_SOLID, 1, pDC->GetTextColor());
450 CPen * penOld = pDC->SelectObject(&pen);
451 pDC->MoveTo(xStart, y);
452 pDC->LineTo(xEnd, y);
453 pDC->SelectObject(penOld);
454 pen.DeleteObject();
457 BOOL CHTMLFormatter::IsPointOverALink(CPoint pt)
459 CRect rect;
460 for (int i=0; i<m_arLinkRects.GetCount(); i++)
462 rect = m_arLinkRects.GetAt(i);
463 if (rect.PtInRect(pt))
464 return TRUE;
466 return FALSE;
469 CString CHTMLFormatter::GetLinkForPoint(CPoint pt)
471 CRect rect;
472 for (int i=0; i<m_arLinkRects.GetCount(); i++)
474 rect = m_arLinkRects.GetAt(i);
475 if (rect.PtInRect(pt))
477 return m_arLinkURLs.GetAt(i);
480 return _T("");