1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2011, 2015 - TortoiseSVN
4 // Copyright (C) 2012-2013, 2015-2018 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "TortoiseProc.h"
23 #include "RevisionGraphDlg.h"
26 #include "UnicodeUtils.h"
28 //#include "SVNInfo.h"
29 //#include "SVNDiff.h"
30 #include "RevisionGraphWnd.h"
31 //#include "IRevisionGraphLayout.h"
32 //#include "UpsideDownLayout.h"
33 //#include "ShowTreeStripes.h"
35 #include "UnicodeUtils.h"
41 static char THIS_FILE
[] = __FILE__
;
44 using namespace Gdiplus
;
46 Color
GetColorFromSysColor(int nIndex
)
49 color
.SetFromCOLORREF(GetSysColor(nIndex
));
53 /************************************************************************/
54 /* Graphing functions */
55 /************************************************************************/
56 CFont
* CRevisionGraphWnd::GetFont(BOOL bItalic
/*= FALSE*/, BOOL bBold
/*= FALSE*/)
63 if (!m_apFonts
[nIndex
])
65 m_apFonts
[nIndex
] = new CFont
;
66 m_lfBaseFont
.lfWeight
= bBold
? FW_BOLD
: FW_NORMAL
;
67 m_lfBaseFont
.lfItalic
= (BYTE
) bItalic
;
68 m_lfBaseFont
.lfStrikeOut
= (BYTE
) FALSE
;
69 m_lfBaseFont
.lfHeight
= -CDPIAware::Instance().PointsToPixelsY(m_nFontSize
);
70 // use the empty font name, so GDI takes the first font which matches
71 // the specs. Maybe this will help render chinese/japanese chars correctly.
72 wcsncpy_s(m_lfBaseFont
.lfFaceName
, L
"MS Shell Dlg 2", _countof(m_lfBaseFont
.lfFaceName
) - 1);
73 if (!m_apFonts
[nIndex
]->CreateFontIndirect(&m_lfBaseFont
))
75 delete m_apFonts
[nIndex
];
76 m_apFonts
[nIndex
] = nullptr;
77 return CWnd::GetFont();
80 return m_apFonts
[nIndex
];
83 BOOL
CRevisionGraphWnd::OnEraseBkgnd(CDC
* /*pDC*/)
88 void CRevisionGraphWnd::OnPaint()
90 CPaintDC
dc(this); // device context for painting
91 CRect rect
= GetClientRect();
93 if (IsUpdateJobRunning())
95 CString fetch
= CString(MAKEINTRESOURCE(IDS_PROC_LOADING
));
96 dc
.FillSolidRect(rect
, ::GetSysColor(COLOR_APPWORKSPACE
));
97 dc
.ExtTextOut(20, 20, ETO_CLIPPED
, nullptr, fetch
, nullptr);
101 }else if (this->m_Graph
.empty())
103 CString sNoGraphText
;
104 sNoGraphText
.LoadString(IDS_REVGRAPH_ERR_NOGRAPH
);
105 dc
.FillSolidRect(rect
, RGB(255,255,255));
106 dc
.ExtTextOut(20, 20, ETO_CLIPPED
, nullptr, sNoGraphText
, nullptr);
112 DrawGraph(dev
, rect
, GetScrollPos(SB_VERT
), GetScrollPos(SB_HORZ
), false);
115 void CRevisionGraphWnd::CutawayPoints (const RectF
& rect
, float cutLen
, TCutRectangle
& result
)
117 result
[0] = PointF (rect
.X
, rect
.Y
+ cutLen
);
118 result
[1] = PointF (rect
.X
+ cutLen
, rect
.Y
);
119 result
[2] = PointF (rect
.GetRight() - cutLen
, rect
.Y
);
120 result
[3] = PointF (rect
.GetRight(), rect
.Y
+ cutLen
);
121 result
[4] = PointF (rect
.GetRight(), rect
.GetBottom() - cutLen
);
122 result
[5] = PointF (rect
.GetRight() - cutLen
, rect
.GetBottom());
123 result
[6] = PointF (rect
.X
+ cutLen
, rect
.GetBottom());
124 result
[7] = PointF (rect
.X
, rect
.GetBottom() - cutLen
);
127 void CRevisionGraphWnd::DrawRoundedRect (GraphicsDevice
& graphics
, const Color
& penColor
, int penWidth
, const Pen
* pen
, const Color
& fillColor
, const Brush
* brush
, const RectF
& rect
, int mask
)
129 enum {POINT_COUNT
= 8};
131 float radius
= CORNER_SIZE
* m_fZoomFactor
;
132 PointF points
[POINT_COUNT
];
133 CutawayPoints (rect
, radius
, points
);
135 if (graphics
.graphics
)
141 path
.AddArc (points
[0].X
, points
[1].Y
, radius
, radius
, 180, 90);
142 path
.AddArc (points
[2].X
, points
[2].Y
, radius
, radius
, 270, 90);
144 path
.AddLine(points
[0].X
, points
[1].Y
, points
[3].X
, points
[2].Y
);
146 if(mask
& ROUND_DOWN
)
148 path
.AddArc (points
[5].X
, points
[4].Y
, radius
, radius
, 0, 90);
149 path
.AddArc (points
[7].X
, points
[7].Y
, radius
, radius
, 90, 90);
152 path
.AddLine(points
[3].X
, points
[3].Y
, points
[4].X
, points
[5].Y
);
153 path
.AddLine(points
[4].X
, points
[5].Y
, points
[7].X
, points
[6].Y
);
156 points
[0].Y
-= radius
/ 2;
157 path
.AddLine (points
[7], points
[0]);
160 graphics
.graphics
->FillPath (brush
, &path
);
162 graphics
.graphics
->DrawPath (pen
, &path
);
164 else if (graphics
.pSVG
)
165 graphics
.pSVG
->RoundedRectangle((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
, penColor
, penWidth
, fillColor
, (int)radius
, mask
);
168 void CRevisionGraphWnd::DrawOctangle (GraphicsDevice
& graphics
, const Color
& penColor
, int penWidth
, const Pen
* pen
, const Color
& fillColor
, const Brush
* brush
, const RectF
& rect
)
170 enum {POINT_COUNT
= 8};
172 // show left & right edges of low boxes as "<===>"
174 float minCutAway
= min (CORNER_SIZE
* m_fZoomFactor
, rect
.Height
/ 2);
176 // larger boxes: remove 25% of the shorter side
178 float suggestedCutAway
= min (rect
.Height
, rect
.Width
) / 4;
180 // use the more visible one of the former two
182 PointF points
[POINT_COUNT
];
183 CutawayPoints (rect
, max (minCutAway
, suggestedCutAway
), points
);
187 if (graphics
.graphics
)
190 graphics
.graphics
->FillPolygon (brush
, points
, POINT_COUNT
);
192 graphics
.graphics
->DrawPolygon (pen
, points
, POINT_COUNT
);
194 else if (graphics
.pSVG
)
196 graphics
.pSVG
->Polygon(points
, POINT_COUNT
, penColor
, penWidth
, fillColor
);
200 inline BYTE
LimitedScaleColor (BYTE c1
, BYTE c2
, float factor
)
202 BYTE scaled
= c2
+ (BYTE
)((c1
-c2
)*factor
);
208 Color
LimitedScaleColor (const Color
& c1
, const Color
& c2
, float factor
)
210 return Color ( LimitedScaleColor (c1
.GetA(), c2
.GetA(), factor
)
211 , LimitedScaleColor (c1
.GetR(), c2
.GetR(), factor
)
212 , LimitedScaleColor (c1
.GetG(), c2
.GetG(), factor
)
213 , LimitedScaleColor (c1
.GetB(), c2
.GetB(), factor
));
216 RectF
CRevisionGraphWnd::TransformRectToScreen (const CRect
& rect
, const CSize
& offset
) const
218 PointF
leftTop ( rect
.left
* m_fZoomFactor
219 , rect
.top
* m_fZoomFactor
);
220 return RectF ( leftTop
.X
- offset
.cx
221 , leftTop
.Y
- offset
.cy
222 , rect
.right
* m_fZoomFactor
- leftTop
.X
- 1
223 , rect
.bottom
* m_fZoomFactor
- leftTop
.Y
);
227 RectF
CRevisionGraphWnd::GetNodeRect(const ogdf::node
& node
, const CSize
& offset
) const
229 // get node and position
232 rect
.left
= (int) (this->m_GraphAttr
.x(node
) - m_GraphAttr
.width(node
)/2);
233 rect
.top
= (int) (this->m_GraphAttr
.y(node
) - m_GraphAttr
.height(node
)/2);
234 rect
.bottom
= (int)( rect
.top
+ m_GraphAttr
.height(node
));
235 rect
.right
= (int)(rect
.left
+ m_GraphAttr
.width(node
));
237 RectF
noderect (TransformRectToScreen (rect
, offset
));
239 // show two separate lines for touching nodes,
240 // unless the scale is too small
242 if (noderect
.Height
> 15.0f
)
243 noderect
.Height
-= 1.0f
;
250 void CRevisionGraphWnd::DrawSquare
251 ( GraphicsDevice
& graphics
252 , const PointF
& leftTop
253 , const Color
& lightColor
254 , const Color
& darkColor
255 , const Color
& penColor
)
257 float squareSize
= MARKER_SIZE
* m_fZoomFactor
;
259 PointF
leftBottom (leftTop
.X
, leftTop
.Y
+ squareSize
);
260 RectF
square (leftTop
, SizeF (squareSize
, squareSize
));
262 if (graphics
.graphics
)
264 LinearGradientBrush
lgBrush (leftTop
, leftBottom
, lightColor
, darkColor
);
265 graphics
.graphics
->FillRectangle (&lgBrush
, square
);
266 if (squareSize
> 4.0f
)
269 graphics
.graphics
->DrawRectangle (&pen
, square
);
272 else if (graphics
.pSVG
)
274 graphics
.pSVG
->GradientRectangle((int)square
.X
, (int)square
.Y
, (int)square
.Width
, (int)square
.Height
,
275 lightColor
, darkColor
, penColor
);
279 void CRevisionGraphWnd::DrawMarker
280 ( GraphicsDevice
& graphics
281 , const RectF
& noderect
282 , MarkerPosition
/*position*/
283 , int /*relPosition*/
284 , const Color
& penColor
287 REAL width
= 4*this->m_fZoomFactor
<1? 1: 4*this->m_fZoomFactor
;
288 Pen
pen(penColor
,width
);
289 DrawRoundedRect(graphics
, penColor
, (int)width
, &pen
, Color(0,0,0), nullptr, noderect
);
293 REAL x
= max(1, 10 * this->m_fZoomFactor
);
294 REAL y1
= max(1, 25 * this->m_fZoomFactor
);
295 REAL y2
= max(1, 5 * this->m_fZoomFactor
);
296 if(graphics
.graphics
)
298 graphics
.graphics
->DrawLine(&pen
, noderect
.X
+ x
, noderect
.Y
- y1
, noderect
.X
+ x
, noderect
.Y
- y2
);
299 if (m_SelectedEntry2
)
302 base
.AppendFormat(IDS_PROC_DIFF_BASE
);
304 SolidBrush
blackbrush(penColor
);
305 Gdiplus::Font
font(CAppUtils::GetLogFontName(), (REAL
)m_nFontSize
, FontStyleRegular
);
306 graphics
.graphics
->DrawString(base
, base
.GetLength(), &font
, Gdiplus::PointF(noderect
.X
+ x
+ width
, noderect
.Y
- y1
), &blackbrush
);
313 REAL x1
= max(1, 5 * this->m_fZoomFactor
);
314 REAL x2
= max(1, 15 * this->m_fZoomFactor
);
315 REAL y1
= max(1, 25 * this->m_fZoomFactor
);
316 REAL y2
= max(1, 5 * this->m_fZoomFactor
);
317 if(graphics
.graphics
)
319 graphics
.graphics
->DrawLine(&pen
, noderect
.X
+ x1
, noderect
.Y
- y1
, noderect
.X
+ x1
, noderect
.Y
- y2
);
320 graphics
.graphics
->DrawLine(&pen
, noderect
.X
+ x2
, noderect
.Y
- y1
, noderect
.X
+ x2
, noderect
.Y
- y2
);
325 PointF
CRevisionGraphWnd::cutPoint(ogdf::node v
, double lw
, PointF ps
, PointF pt
)
327 double x
= m_GraphAttr
.x(v
);
328 double y
= m_GraphAttr
.y(v
);
329 double xmin
= x
- this->m_GraphAttr
.width(v
)/2 - lw
/2;
330 double xmax
= x
+ this->m_GraphAttr
.width(v
)/2 + lw
/2;
331 double ymin
= y
- this->m_GraphAttr
.height(v
)/2 - lw
/2;
332 double ymax
= y
+ this->m_GraphAttr
.height(v
)/2 + lw
/2;;
334 double dx
= pt
.X
- ps
.X
;
335 double dy
= pt
.Y
- ps
.Y
;
340 double t
= (ymax
-ps
.Y
) / dy
;
343 if(xmin
<= x
&& x
<= xmax
)
344 return PointF((REAL
)x
, (REAL
)ymax
);
347 } else if(pt
.Y
< ymin
) {
348 double t
= (ymin
-ps
.Y
) / dy
;
351 if(xmin
<= x
&& x
<= xmax
)
352 return PointF((REAL
)x
, (REAL
)ymin
);
359 double t
= (xmax
-ps
.X
) / dx
;
362 if(ymin
<= y
&& y
<= ymax
)
363 return PointF((REAL
)xmax
, (REAL
)y
);
366 } else if(pt
.X
< xmin
) {
367 double t
= (xmin
-ps
.X
) / dx
;
370 if(ymin
<= y
&& y
<= ymax
)
371 return PointF((REAL
)xmin
, (REAL
)y
);
378 void CRevisionGraphWnd::DrawConnections (GraphicsDevice
& graphics
, const CRect
& /*logRect*/, const CSize
& offset
)
380 CArray
<PointF
> points
;
383 if(graphics
.graphics
)
384 graphics
.graphics
->SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias
);
386 float penwidth
= 2*m_fZoomFactor
<1? 1:2*m_fZoomFactor
;
387 Gdiplus::Pen
pen(GetColorFromSysColor(COLOR_WINDOWTEXT
), penwidth
);
389 // iterate over all visible lines
391 forall_edges(e
, m_Graph
)
393 // get connection and point position
394 const auto& dpl
= this->m_GraphAttr
.bends(e
);
400 pt
.X
= (REAL
)m_GraphAttr
.x(e
->source());
401 pt
.Y
= (REAL
)m_GraphAttr
.y(e
->source());
405 for (auto it
= dpl
.begin(); it
.valid(); ++it
)
407 pt
.X
= (REAL
)(*it
).m_x
;
408 pt
.Y
= (REAL
)(*it
).m_y
;
412 pt
.X
= (REAL
)m_GraphAttr
.x(e
->target());
413 pt
.Y
= (REAL
)m_GraphAttr
.y(e
->target());
417 points
[0] = this->cutPoint(e
->source(), 1, points
[0], points
[1]);
418 points
[points
.GetCount()-1] = this->cutPoint(e
->target(), 1, points
[points
.GetCount()-1], points
[points
.GetCount()-2]);
419 // draw the connection
421 for (int i
= 0; i
< points
.GetCount(); ++i
)
424 points
[i
].X
= points
[i
].X
* this->m_fZoomFactor
- offset
.cx
;
425 points
[i
].Y
= points
[i
].Y
* this->m_fZoomFactor
- offset
.cy
;
429 if (graphics
.graphics
)
430 graphics
.graphics
->DrawLines(&pen
, points
.GetData(), (INT
)points
.GetCount());
431 else if (graphics
.pSVG
)
432 graphics
.pSVG
->Polyline(points
.GetData(), (int)points
.GetCount(), Color(0,0,0), (int)penwidth
);
433 else if (graphics
.pGraphviz
)
435 CString hash1
= L
'g' + m_logEntries
[e
->target()->index()].ToString().Left(g_Git
.GetShortHASHLength());
436 CString hash2
= L
'g' + m_logEntries
[e
->source()->index()].ToString().Left(g_Git
.GetShortHASHLength());
437 graphics
.pGraphviz
->DrawEdge(hash1
, hash2
);
441 double dx
= points
[1].X
- points
[0].X
;
442 double dy
= points
[1].Y
- points
[0].Y
;
444 double len
= sqrt(dx
*dx
+ dy
*dy
);
445 dx
= m_ArrowSize
* m_fZoomFactor
*dx
/len
;
446 dy
= m_ArrowSize
* m_fZoomFactor
*dy
/len
;
448 double p1_x
, p1_y
, p2_x
, p2_y
;
449 p1_x
= dx
* m_ArrowCos
- dy
* m_ArrowSin
;
450 p1_y
= dx
* m_ArrowSin
+ dy
* m_ArrowCos
;
452 p2_x
= dx
* m_ArrowCos
+ dy
* m_ArrowSin
;
453 p2_y
= -dx
* m_ArrowSin
+ dy
* m_ArrowCos
;
455 //graphics.graphics->DrawLine(&pen, points[0].X,points[0].Y, points[0].X +p1_x,points[0].Y+p1_y);
456 //graphics.graphics->DrawLine(&pen, points[0].X,points[0].Y, points[0].X +p2_x,points[0].Y+p2_y);
460 arrows
[0].X
= points
[0].X
;
461 arrows
[0].Y
= points
[0].Y
;
463 arrows
[1].X
= points
[0].X
+ (REAL
)p1_x
;
464 arrows
[1].Y
= points
[0].Y
+ (REAL
)p1_y
;
466 arrows
[2].X
= points
[0].X
+ (REAL
)dx
*3/5;
467 arrows
[2].Y
= points
[0].Y
+ (REAL
)dy
*3/5;
469 arrows
[3].X
= points
[0].X
+ (REAL
)p2_x
;
470 arrows
[3].Y
= points
[0].Y
+ (REAL
)p2_y
;
472 arrows
[4].X
= points
[0].X
;
473 arrows
[4].Y
= points
[0].Y
;
475 path
.AddLines(arrows
, 5);
476 path
.SetFillMode(FillModeAlternate
);
477 if(graphics
.graphics
)
478 graphics
.graphics
->DrawPath(&pen
, &path
);
479 else if(graphics
.pSVG
)
480 graphics
.pSVG
->DrawPath(arrows
, 5, Color(0,0,0), (int)penwidth
, Color(0,0,0));
484 void CRevisionGraphWnd::DrawTexts (GraphicsDevice
& graphics
, const CRect
& /*logRect*/, const CSize
& offset
)
486 //COLORREF standardTextColor = GetSysColor(COLOR_WINDOWTEXT);
487 if (m_nFontSize
<= 0)
490 // iterate over all visible nodes
493 graphics
.pDC
->SetTextAlign (TA_CENTER
| TA_TOP
);
495 CString fontname
= CAppUtils::GetLogFontName();
497 Gdiplus::Font
font(fontname
, (REAL
)m_nFontSize
, FontStyleRegular
);
498 SolidBrush
blackbrush((ARGB
)Color::Black
);
500 DWORD revGraphUseLocalForCur
= CRegDWORD(L
"Software\\TortoiseGit\\TortoiseProc\\Graph\\RevGraphUseLocalForCur");
503 forall_nodes(v
,m_Graph
)
505 // get node and position
506 RectF
noderect (GetNodeRect (v
, offset
));
508 // draw the revision text
509 CGitHash hash
= this->m_logEntries
[v
->index()];
510 double hight
= noderect
.Height
/ (!m_HashMap
[hash
].empty() ? m_HashMap
[hash
].size() : 1);
512 if (m_HashMap
.find(hash
) == m_HashMap
.end() || m_HashMap
[hash
].empty())
515 background
.SetFromCOLORREF (GetSysColor(COLOR_WINDOW
));
516 Gdiplus::Pen
pen(background
,1.0F
);
517 Color brightColor
= LimitedScaleColor (background
, RGB(255,0,0), 0.9f
);
518 Gdiplus::SolidBrush
brush(brightColor
);
520 DrawRoundedRect(graphics
, background
,1, &pen
, brightColor
, &brush
, noderect
);
522 if(graphics
.graphics
)
524 graphics
.graphics
->DrawString(hash
.ToString().Left(g_Git
.GetShortHASHLength()),-1,
526 Gdiplus::PointF(noderect
.X
+ this->GetLeftRightMargin()*this->m_fZoomFactor
,noderect
.Y
+this->GetTopBottomMargin()*m_fZoomFactor
),
531 graphics
.pSVG
->Text((int)(noderect
.X
+ this->GetLeftRightMargin() * this->m_fZoomFactor
),
532 (int)(noderect
.Y
+ this->GetTopBottomMargin() * m_fZoomFactor
+ m_nFontSize
),
533 CUnicodeUtils::GetUTF8(fontname
), m_nFontSize
, false, false, (ARGB
)Color::Black
,
534 CUnicodeUtils::GetUTF8(hash
.ToString().Left(g_Git
.GetShortHASHLength())));
536 if (graphics
.pGraphviz
)
538 CString shortHash
= hash
.ToString().Left(g_Git
.GetShortHASHLength());
539 graphics
.pGraphviz
->DrawNode(L
'g' + shortHash
, shortHash
, fontname
, m_nFontSize
, background
, brightColor
, (int)noderect
.Height
);
543 if (graphics
.pGraphviz
)
545 CString id
= L
'g' + hash
.ToString().Left(g_Git
.GetShortHASHLength());
546 graphics
.pGraphviz
->BeginDrawTableNode(id
, fontname
, m_nFontSize
, (int)noderect
.Height
);
549 for (size_t i
= 0; i
< m_HashMap
[hash
].size(); ++i
)
552 CString str
= m_HashMap
[hash
][i
];
555 rect
.X
= (REAL
)noderect
.X
;
556 rect
.Y
= (REAL
)(noderect
.Y
+ hight
*i
);
557 rect
.Width
= (REAL
)noderect
.Width
;
558 rect
.Height
= (REAL
)hight
;
560 COLORREF colRef
= m_Colors
.GetColor(CColors::OtherRef
);
562 CGit::REF_TYPE refType
;
563 shortname
= CGit::GetShortName(str
, &refType
);
566 case CGit::REF_TYPE::LOCAL_BRANCH
:
567 if (!revGraphUseLocalForCur
&& shortname
== m_CurrentBranch
)
568 colRef
= m_Colors
.GetColor(CColors::CurrentBranch
);
570 colRef
= m_Colors
.GetColor(CColors::LocalBranch
);
572 case CGit::REF_TYPE::REMOTE_BRANCH
:
573 colRef
= m_Colors
.GetColor(CColors::RemoteBranch
);
575 case CGit::REF_TYPE::ANNOTATED_TAG
:
576 case CGit::REF_TYPE::TAG
:
577 colRef
= m_Colors
.GetColor(CColors::Tag
);
579 case CGit::REF_TYPE::STASH
:
580 colRef
= m_Colors
.GetColor(CColors::Stash
);
582 case CGit::REF_TYPE::BISECT_GOOD
:
583 colRef
= m_Colors
.GetColor(CColors::BisectGood
);
585 case CGit::REF_TYPE::BISECT_BAD
:
586 colRef
= m_Colors
.GetColor(CColors::BisectBad
);
588 case CGit::REF_TYPE::BISECT_SKIP
:
589 colRef
= m_Colors
.GetColor(CColors::BisectSkip
);
591 case CGit::REF_TYPE::NOTES
:
592 colRef
= m_Colors
.GetColor(CColors::NoteNode
);
596 Gdiplus::Color
color(GetRValue(colRef
), GetGValue(colRef
), GetBValue(colRef
));
597 Gdiplus::Pen
pen(color
);
598 Gdiplus::SolidBrush
brush(color
);
601 mask
|= (i
==0)? ROUND_UP
:0;
602 mask
|= (i
== m_HashMap
[hash
].size()-1)? ROUND_DOWN
:0;
603 this->DrawRoundedRect(graphics
, color
,1,&pen
, color
,&brush
, rect
,mask
);
605 if (graphics
.graphics
)
607 //graphics.graphics->FillRectangle(&SolidBrush(Gdiplus::Color(GetRValue(colRef), GetGValue(colRef), GetBValue(colRef))),
610 graphics
.graphics
->DrawString(shortname
, shortname
.GetLength(),
612 Gdiplus::PointF((REAL
)(noderect
.X
+ this->GetLeftRightMargin()*m_fZoomFactor
),
613 (REAL
)(noderect
.Y
+ this->GetTopBottomMargin()*m_fZoomFactor
+ hight
*i
)),
616 //graphics.graphics->DrawString(shortname.GetBuffer(), shortname.GetLength(), ::new Gdiplus::Font(graphics.pDC->m_hDC), PointF(noderect.X, noderect.Y + hight * i), nullptr, nullptr);
619 else if (graphics
.pSVG
)
620 graphics
.pSVG
->Text((int)(noderect
.X
+ this->GetLeftRightMargin() * m_fZoomFactor
),
621 (int)(noderect
.Y
+ this->GetTopBottomMargin() * m_fZoomFactor
+ hight
* i
+ m_nFontSize
),
622 CUnicodeUtils::GetUTF8(fontname
), m_nFontSize
,
623 false, false, (ARGB
)Color::Black
, CUnicodeUtils::GetUTF8(shortname
));
624 else if (graphics
.pGraphviz
)
625 graphics
.pGraphviz
->DrawTableNode(shortname
, color
);
628 if (graphics
.pGraphviz
)
629 graphics
.pGraphviz
->EndDrawTableNode();
631 if ((m_SelectedEntry1
== v
))
632 DrawMarker(graphics
, noderect
, mpLeft
, 0, GetColorFromSysColor(COLOR_HIGHLIGHT
), 1);
634 if ((m_SelectedEntry2
== v
))
635 DrawMarker(graphics
, noderect
, mpLeft
, 0, Color(136,0, 21), 2);
639 void CRevisionGraphWnd::DrawGraph(GraphicsDevice
& graphics
, const CRect
& rect
, int nVScrollPos
, int nHScrollPos
, bool bDirectDraw
)
641 CMemDC
* memDC
= nullptr;
646 memDC
= new CMemDC (*graphics
.pDC
, rect
);
647 graphics
.pDC
= &memDC
->GetDC();
650 graphics
.pDC
->FillSolidRect(rect
, GetSysColor(COLOR_WINDOW
));
651 graphics
.pDC
->SetBkMode(TRANSPARENT
);
656 CSize
offset (nHScrollPos
, nVScrollPos
);
657 CRect
logRect ( (int)(offset
.cx
/ m_fZoomFactor
)-1
658 , (int)(offset
.cy
/ m_fZoomFactor
)-1
659 , (int)((rect
.Width() + offset
.cx
) / m_fZoomFactor
) + 1
660 , (int)((rect
.Height() + offset
.cy
) / m_fZoomFactor
) + 1);
662 // draw the different components
666 Graphics
* gcs
= Graphics::FromHDC(*graphics
.pDC
);
667 graphics
.graphics
= gcs
;
668 gcs
->SetPageUnit (UnitPixel
);
669 gcs
->SetInterpolationMode (InterpolationModeHighQualityBicubic
);
670 gcs
->SetSmoothingMode(SmoothingModeAntiAlias
);
671 gcs
->SetClip(RectF(Gdiplus::REAL(rect
.left
), Gdiplus::REAL(rect
.top
), Gdiplus::REAL(rect
.Width()), Gdiplus::REAL(rect
.Height())));
674 Bitmap
glyphs (AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_REVGRAPHGLYPHS
));
676 DrawTexts (graphics
, logRect
, offset
);
677 DrawConnections (graphics
, logRect
, offset
);
680 if ((!bDirectDraw
)&&(m_Preview
.GetSafeHandle())&&(m_bShowOverview
)&&(graphics
.pDC
))
682 // draw the overview image rectangle in the top right corner
683 CMyMemDC
memDC2(graphics
.pDC
, true);
684 memDC2
.SetWindowOrg(0, 0);
685 HBITMAP oldhbm
= (HBITMAP
)memDC2
.SelectObject(&m_Preview
);
686 graphics
.pDC
->BitBlt(rect
.Width()-m_previewWidth
, rect
.Height() - m_previewHeight
, m_previewWidth
, m_previewHeight
,
687 &memDC2
, 0, 0, SRCCOPY
);
688 memDC2
.SelectObject(oldhbm
);
689 // draw the border for the overview rectangle
690 m_OverviewRect
.left
= rect
.Width()-m_previewWidth
;
691 m_OverviewRect
.top
= rect
.Height()- m_previewHeight
;
692 m_OverviewRect
.right
= rect
.Width();
693 m_OverviewRect
.bottom
= rect
.Height();
694 graphics
.pDC
->DrawEdge(&m_OverviewRect
, EDGE_BUMP
, BF_RECT
);
695 // now draw a rectangle where the current view is located in the overview
697 LONG width
= (long)(rect
.Width() * m_previewZoom
/ m_fZoomFactor
);
698 LONG height
= (long)(rect
.Height() * m_previewZoom
/ m_fZoomFactor
);
699 LONG xpos
= (long)(nHScrollPos
* m_previewZoom
/ m_fZoomFactor
);
700 LONG ypos
= (long)(nVScrollPos
* m_previewZoom
/ m_fZoomFactor
);
702 tempRect
.left
= rect
.Width()-m_previewWidth
+xpos
;
703 tempRect
.top
= rect
.Height() - m_previewHeight
+ ypos
;
704 tempRect
.right
= tempRect
.left
+ width
;
705 tempRect
.bottom
= tempRect
.top
+ height
;
706 // make sure the position rect is not bigger than the preview window itself
707 ::IntersectRect(&m_OverviewPosRect
, &m_OverviewRect
, &tempRect
);
709 RectF
rect2 ( (float)m_OverviewPosRect
.left
, (float)m_OverviewPosRect
.top
710 , (float)m_OverviewPosRect
.Width(), (float)m_OverviewPosRect
.Height());
711 if (graphics
.graphics
)
713 SolidBrush
brush (Color (64, 0, 0, 0));
714 graphics
.graphics
->FillRectangle (&brush
, rect2
);
715 graphics
.pDC
->DrawEdge(&m_OverviewPosRect
, EDGE_BUMP
, BF_RECT
);
719 // flush changes to screen
721 delete graphics
.graphics
;
725 void CRevisionGraphWnd::SetNodeRect(GraphicsDevice
& graphics
, ogdf::node
*pnode
, CGitHash rev
, int mode
)
727 //multi - line mode. One RefName is one new line
728 CString fontname
= CAppUtils::GetLogFontName();
731 if(this->m_HashMap
.find(rev
) == m_HashMap
.end())
733 CString shorthash
= rev
.ToString().Left(g_Git
.GetShortHASHLength());
735 if(graphics
.graphics
)
737 //GetTextExtentPoint32(graphics.pDC->m_hDC, shorthash.GetBuffer(), shorthash.GetLength(), &size);
738 Gdiplus::Font
font(fontname
, (REAL
)m_nFontSize
, FontStyleRegular
);
739 graphics
.graphics
->MeasureString(shorthash
, shorthash
.GetLength(),
741 Gdiplus::PointF(0,0), &rect
);
743 m_GraphAttr
.width(*pnode
) = this->GetLeftRightMargin()*2 + rect
.Width
;
744 m_GraphAttr
.height(*pnode
) = this->GetTopBottomMargin()*2 + rect
.Height
;
751 for (size_t i
= 0; i
< m_HashMap
[rev
].size(); ++i
)
754 CString shortref
= m_HashMap
[rev
][i
];
755 shortref
= CGit::GetShortName(shortref
, nullptr);
758 Gdiplus::Font
font(fontname
, (REAL
)m_nFontSize
, FontStyleRegular
);
759 graphics
.graphics
->MeasureString(shortref
, shortref
.GetLength(),
761 Gdiplus::PointF(0,0), &rect
);
762 if(rect
.Width
> xmax
)
764 if(rect
.Height
> ymax
)
769 m_GraphAttr
.width(*pnode
) = this->GetLeftRightMargin()*2 + xmax
;
770 m_GraphAttr
.height(*pnode
) = (this->GetTopBottomMargin()*2 + ymax
) * lines
;