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.
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
;
35 STATEMACHINE nState
= BEGIN_TAG
;
36 int nAlign
= ALIGN_LEFT
;
37 BOOL bCloseTag
= FALSE
;
39 m_arLinkRects
.RemoveAll();
40 m_arLinkURLs
.RemoveAll();
47 CPoint pt
= rect
.TopLeft();
51 COLORREF crText
= pDC
->GetTextColor();
52 COLORREF crBg
= pDC
->GetBkColor();
55 memcpy(&lf
, &font
, sizeof(LOGFONT
));
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
= _T("");
68 CString strText
= _T("");
72 linkRect
.SetRectEmpty();
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
89 //waiting for the begin of a tag (<tag>), newline ('\n' or '\r') or tab ('\t')
93 nState
= TEXT_TAG
; //statemachine to 'waiting for the tag'
94 bCloseTag
= FALSE
; //opening bracket
108 strText
+= str
.GetAt(i
);
113 //get the tag itself (until the closing bracket ('>'))
114 switch (str
.GetAt(i
))
117 if (strTag
.IsEmpty())
118 bCloseTag
= TRUE
; //found the char's cancel tag
121 if (strTag
.IsEmpty())
124 strText
+= str
.GetAt(i
);
126 else strTag
+= str
.GetAt(i
);
133 if (strTag
.CompareNoCase(_T("b"))==0)
139 else if (strTag
.CompareNoCase(_T("i")) == 0)
145 else if (strTag
.CompareNoCase(_T("s")) == 0)
151 else if (strTag
.CompareNoCase(_T("u")) == 0)
157 else if (strTag
.CompareNoCase(_T("t")) == 0)
162 nState
= BEGIN_NUMBER
;
164 else if (strTag
.CompareNoCase(_T("ct")) == 0)
169 nState
= BEGIN_NUMBER
;
171 else if (strTag
.CompareNoCase(_T("cb")) == 0)
173 //Color of the background
176 nState
= BEGIN_NUMBER
;
178 else if (strTag
.CompareNoCase(_T("al")) == 0)
184 else if (strTag
.CompareNoCase(_T("ac")) == 0)
188 nAlign
= bCloseTag
? ALIGN_LEFT
: ALIGN_CENTER
;
191 else if (strTag
.CompareNoCase(_T("ar")) == 0)
195 nAlign
= bCloseTag
? ALIGN_LEFT
: ALIGN_RIGHT
;
198 else if (strTag
.CompareNoCase(_T("hr")) == 0)
201 nCmd
= HORZ_LINE_PERCENT
;
203 nState
= BEGIN_NUMBER
;
205 else if (strTag
.CompareNoCase(_T("a")) == 0)
209 nState
= BEGIN_URL
; //wait for '='
211 else nState
= END_TAG
; //Unknown tag
214 strTag
+= str
.GetAt(i
);
219 //waiting for the end of the tag
220 if (str
.GetAt(i
) == _T('>'))
224 //waiting for the start of a number
225 if (str
.GetAt(i
) == _T('='))
228 nState
= TEXT_NUMBER
;
230 else if (str
.GetAt(i
) == _T('>'))
231 nState
= BEGIN_TAG
; //not a number
234 //waiting for the start of a number
235 if (str
.GetAt(i
) == _T('='))
240 else if (str
.GetAt(i
) == _T('>'))
241 nState
= BEGIN_TAG
; //not a url
244 //waiting for a number string
245 switch (str
.GetAt(i
))
249 //intended fall through!
251 //Gets the real number from the string
252 if (!strTag
.IsEmpty())
253 nParam
= _tcstoul(strTag
, 0, 0);
257 strTag
+= str
.GetAt(i
);
263 switch (str
.GetAt(i
))
267 if (!strTag
.IsEmpty())
268 m_arLinkURLs
.Add(strTag
);
272 strTag
+= str
.GetAt(i
);
279 //Immitates new line at the end of the string
285 if ((nState
== BEGIN_TAG
) && (nCmd
!= NONE
))
287 //New Command with full parameters
288 if (!strText
.IsEmpty())
295 ptCur
.x
= pt
.x
+ (rect
.Width() - nLengthLines
.GetAt(nLine
)) / 2;
298 ptCur
.x
= pt
.x
+ rect
.Width() - nLengthLines
.GetAt(nLine
);
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
;
311 bFirstOutput
= FALSE
;
321 m_arLinkRects
.Add(linkRect
);
322 linkRect
.SetRectEmpty();
327 pDC
->SelectObject(pOldFont
);
328 tempFont
.DeleteObject();
329 lf
.lfWeight
= font
.lfWeight
;
333 if (lf
.lfWeight
> FW_BLACK
)
334 lf
.lfWeight
= FW_BLACK
;
336 tempFont
.CreateFontIndirect(&lf
);
337 pDC
->SelectObject(&tempFont
);
341 pDC
->SelectObject(pOldFont
);
342 tempFont
.DeleteObject();
343 lf
.lfItalic
= bCloseTag
? FALSE
: TRUE
;
344 tempFont
.CreateFontIndirect(&lf
);
345 pDC
->SelectObject(&tempFont
);
349 pDC
->SelectObject(pOldFont
);
350 tempFont
.DeleteObject();
351 lf
.lfStrikeOut
= bCloseTag
? FALSE
: TRUE
;
352 tempFont
.CreateFontIndirect(&lf
);
353 pDC
->SelectObject(&tempFont
);
357 pDC
->SelectObject(pOldFont
);
358 tempFont
.DeleteObject();
359 lf
.lfUnderline
= bCloseTag
? FALSE
: TRUE
;
360 tempFont
.CreateFontIndirect(&lf
);
361 pDC
->SelectObject(&tempFont
);
365 pDC
->SetTextColor((COLORREF
)nParam
);
368 //Color of the background
369 pDC
->SetBkColor((COLORREF
)nParam
);
370 pDC
->SetBkMode(bCloseTag
? TRANSPARENT
: OPAQUE
);
372 case HORZ_LINE_PERCENT
:
373 //Horizontal line with percent length
376 percent
.SetAt(nLine
, percent
.GetAt(nLine
) + nParam
);
379 else nParam
= ::MulDiv(rect
.Width(), nParam
, 100);
381 //Horizontal line with absolute length
382 //If text to output is exist
388 ptCur
.x
= pt
.x
+ (rect
.Width() - nLengthLines
.GetAt(nLine
)) / 2;
391 ptCur
.x
= pt
.x
+ rect
.Width() - nLengthLines
.GetAt(nLine
);
395 DrawHorzLine(pDC
, ptCur
.x
, ptCur
.x
+ nParam
, ptCur
.y
+ nHeight
/ 2);
397 bFirstOutput
= FALSE
;
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
;
415 nTemp
= (ptCur
.x
- pt
.x
) % (nWidth
* 4);
419 ptCur
.x
+= (nWidth
* 4) - nTemp
;
422 ptCur
.x
+= (nParam
* nWidth
* 4);
425 //Resets the last command
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));
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
);
457 BOOL
CHTMLFormatter::IsPointOverALink(CPoint pt
)
460 for (int i
=0; i
<m_arLinkRects
.GetCount(); i
++)
462 rect
= m_arLinkRects
.GetAt(i
);
463 if (rect
.PtInRect(pt
))
469 CString
CHTMLFormatter::GetLinkForPoint(CPoint pt
)
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
);