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::ClearVisibleGlyphs (const CRect
& /*rect*/)
118 float glyphSize
= GLYPH_SIZE
* m_fZoomFactor
;
120 CSyncPointer
<CRevisionGraphState::TVisibleGlyphs
>
121 visibleGlyphs (m_state
.GetVisibleGlyphs());
123 for (size_t i
= visibleGlyphs
->size(), count
= i
; i
> 0; --i
)
125 const PointF
& leftTop
= (*visibleGlyphs
)[i
-1].leftTop
;
126 CRect
glyphRect ( static_cast<int>(leftTop
.X
)
127 , static_cast<int>(leftTop
.Y
)
128 , static_cast<int>(leftTop
.X
+ glyphSize
)
129 , static_cast<int>(leftTop
.Y
+ glyphSize
));
131 if (CRect().IntersectRect (glyphRect
, rect
))
133 (*visibleGlyphs
)[i
-1] = (*visibleGlyphs
)[--count
];
134 visibleGlyphs
->pop_back();
140 void CRevisionGraphWnd::CutawayPoints (const RectF
& rect
, float cutLen
, TCutRectangle
& result
)
142 result
[0] = PointF (rect
.X
, rect
.Y
+ cutLen
);
143 result
[1] = PointF (rect
.X
+ cutLen
, rect
.Y
);
144 result
[2] = PointF (rect
.GetRight() - cutLen
, rect
.Y
);
145 result
[3] = PointF (rect
.GetRight(), rect
.Y
+ cutLen
);
146 result
[4] = PointF (rect
.GetRight(), rect
.GetBottom() - cutLen
);
147 result
[5] = PointF (rect
.GetRight() - cutLen
, rect
.GetBottom());
148 result
[6] = PointF (rect
.X
+ cutLen
, rect
.GetBottom());
149 result
[7] = PointF (rect
.X
, rect
.GetBottom() - cutLen
);
152 void CRevisionGraphWnd::DrawRoundedRect (GraphicsDevice
& graphics
, const Color
& penColor
, int penWidth
, const Pen
* pen
, const Color
& fillColor
, const Brush
* brush
, const RectF
& rect
, int mask
)
154 enum {POINT_COUNT
= 8};
156 float radius
= CORNER_SIZE
* m_fZoomFactor
;
157 PointF points
[POINT_COUNT
];
158 CutawayPoints (rect
, radius
, points
);
160 if (graphics
.graphics
)
166 path
.AddArc (points
[0].X
, points
[1].Y
, radius
, radius
, 180, 90);
167 path
.AddArc (points
[2].X
, points
[2].Y
, radius
, radius
, 270, 90);
169 path
.AddLine(points
[0].X
, points
[1].Y
, points
[3].X
, points
[2].Y
);
171 if(mask
& ROUND_DOWN
)
173 path
.AddArc (points
[5].X
, points
[4].Y
, radius
, radius
, 0, 90);
174 path
.AddArc (points
[7].X
, points
[7].Y
, radius
, radius
, 90, 90);
177 path
.AddLine(points
[3].X
, points
[3].Y
, points
[4].X
, points
[5].Y
);
178 path
.AddLine(points
[4].X
, points
[5].Y
, points
[7].X
, points
[6].Y
);
181 points
[0].Y
-= radius
/ 2;
182 path
.AddLine (points
[7], points
[0]);
185 graphics
.graphics
->FillPath (brush
, &path
);
187 graphics
.graphics
->DrawPath (pen
, &path
);
189 else if (graphics
.pSVG
)
190 graphics
.pSVG
->RoundedRectangle((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
, penColor
, penWidth
, fillColor
, (int)radius
, mask
);
193 void CRevisionGraphWnd::DrawOctangle (GraphicsDevice
& graphics
, const Color
& penColor
, int penWidth
, const Pen
* pen
, const Color
& fillColor
, const Brush
* brush
, const RectF
& rect
)
195 enum {POINT_COUNT
= 8};
197 // show left & right edges of low boxes as "<===>"
199 float minCutAway
= min (CORNER_SIZE
* m_fZoomFactor
, rect
.Height
/ 2);
201 // larger boxes: remove 25% of the shorter side
203 float suggestedCutAway
= min (rect
.Height
, rect
.Width
) / 4;
205 // use the more visible one of the former two
207 PointF points
[POINT_COUNT
];
208 CutawayPoints (rect
, max (minCutAway
, suggestedCutAway
), points
);
212 if (graphics
.graphics
)
215 graphics
.graphics
->FillPolygon (brush
, points
, POINT_COUNT
);
217 graphics
.graphics
->DrawPolygon (pen
, points
, POINT_COUNT
);
219 else if (graphics
.pSVG
)
221 graphics
.pSVG
->Polygon(points
, POINT_COUNT
, penColor
, penWidth
, fillColor
);
227 void CRevisionGraphWnd::DrawShape (GraphicsDevice
& graphics
, const Color
& penColor
, int penWidth
, const Pen
* pen
, const Color
& fillColor
, const Brush
* brush
, const RectF
& rect
, NodeShape shape
)
232 if (graphics
.graphics
)
235 graphics
.graphics
->FillRectangle (brush
, rect
);
237 graphics
.graphics
->DrawRectangle (pen
, rect
);
239 else if (graphics
.pSVG
)
241 graphics
.pSVG
->RoundedRectangle((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
, penColor
, penWidth
, fillColor
);
245 DrawRoundedRect (graphics
, penColor
, penWidth
, pen
, fillColor
, brush
, rect
);
248 DrawOctangle (graphics
, penColor
, penWidth
, pen
, fillColor
, brush
, rect
);
251 if (graphics
.graphics
)
254 graphics
.graphics
->FillEllipse (brush
, rect
);
256 graphics
.graphics
->DrawEllipse(pen
, rect
);
258 else if (graphics
.pSVG
)
259 graphics
.pSVG
->Ellipse((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
, penColor
, penWidth
, fillColor
);
262 ASSERT(FALSE
); //unknown type
268 inline BYTE
LimitedScaleColor (BYTE c1
, BYTE c2
, float factor
)
270 BYTE scaled
= c2
+ (BYTE
)((c1
-c2
)*factor
);
276 Color
LimitedScaleColor (const Color
& c1
, const Color
& c2
, float factor
)
278 return Color ( LimitedScaleColor (c1
.GetA(), c2
.GetA(), factor
)
279 , LimitedScaleColor (c1
.GetR(), c2
.GetR(), factor
)
280 , LimitedScaleColor (c1
.GetG(), c2
.GetG(), factor
)
281 , LimitedScaleColor (c1
.GetB(), c2
.GetB(), factor
));
284 inline BYTE
Darken (BYTE c
)
288 : BYTE(int(2*c
) - 0x100);
291 Color
Darken (const Color
& c
)
296 , Darken (c
.GetB()));
299 BYTE
MaxComponentDiff (const Color
& c1
, const Color
& c2
)
301 int rDiff
= abs ((int)c1
.GetR() - (int)c2
.GetR());
302 int gDiff
= abs ((int)c1
.GetG() - (int)c2
.GetG());
303 int bDiff
= abs ((int)c1
.GetB() - (int)c2
.GetB());
305 return (BYTE
) max (max (rDiff
, gDiff
), bDiff
);
309 void CRevisionGraphWnd::DrawShadow (GraphicsDevice
& graphics
, const RectF
& rect
,
310 Color shadowColor
, NodeShape shape
)
315 shadow
.Offset (2, 2);
317 Pen
pen (shadowColor
);
318 SolidBrush
brush (shadowColor
);
320 DrawShape (graphics
, shadowColor
, 1, &pen
, shadowColor
, &brush
, shadow
, shape
);
324 RectF
CRevisionGraphWnd::TransformRectToScreen (const CRect
& rect
, const CSize
& offset
) const
326 PointF
leftTop ( rect
.left
* m_fZoomFactor
327 , rect
.top
* m_fZoomFactor
);
328 return RectF ( leftTop
.X
- offset
.cx
329 , leftTop
.Y
- offset
.cy
330 , rect
.right
* m_fZoomFactor
- leftTop
.X
- 1
331 , rect
.bottom
* m_fZoomFactor
- leftTop
.Y
);
335 RectF
CRevisionGraphWnd::GetNodeRect(const ogdf::node
& node
, const CSize
& offset
) const
337 // get node and position
340 rect
.left
= (int) (this->m_GraphAttr
.x(node
) - m_GraphAttr
.width(node
)/2);
341 rect
.top
= (int) (this->m_GraphAttr
.y(node
) - m_GraphAttr
.height(node
)/2);
342 rect
.bottom
= (int)( rect
.top
+ m_GraphAttr
.height(node
));
343 rect
.right
= (int)(rect
.left
+ m_GraphAttr
.width(node
));
345 RectF
noderect (TransformRectToScreen (rect
, offset
));
347 // show two separate lines for touching nodes,
348 // unless the scale is too small
350 if (noderect
.Height
> 15.0f
)
351 noderect
.Height
-= 1.0f
;
361 isionGraphWnd::GetBranchCover
362 ( const ILayoutNodeList
* nodeList
365 , const CSize
& offset
)
367 // construct a rect that covers the respective branch
369 CRect
cover (0, 0, 0, 0);
370 while (nodeIndex
!= NO_INDEX
)
372 ILayoutNodeList::SNode node
= nodeList
->GetNode (nodeIndex
);
375 const CVisibleGraphNode
* nextNode
= upward
376 ? node
.node
->GetPrevious()
377 : node
.node
->GetNext();
379 nodeIndex
= !nextNode
? NO_INDEX
: nextNode
->GetIndex();
382 // expand it just a little to make it look nicer
384 cover
.InflateRect (10, 2, 10, 2);
386 // and now, transfrom it
388 return TransformRectToScreen (cover
, offset
);
393 void CRevisionGraphWnd::DrawShadows (GraphicsDevice
& graphics
, const CRect
& logRect
, const CSize
& offset
)
395 // shadow color to use
398 background
.SetFromCOLORREF (GetSysColor(COLOR_WINDOW
));
400 textColor
.SetFromCOLORREF (GetSysColor(COLOR_WINDOWTEXT
));
402 Color shadowColor
= LimitedScaleColor (background
, ARGB (Color::Black
), 0.5f
);
404 // iterate over all visible nodes
406 CSyncPointer
<const ILayoutNodeList
> nodes (m_state
.GetNodes());
407 for ( index_t index
= nodes
->GetFirstVisible (logRect
)
409 ; index
= nodes
->GetNextVisible (index
, logRect
))
411 // get node and position
413 ILayoutNodeList::SNode node
= nodes
->GetNode (index
);
414 RectF
noderect (GetNodeRect (node
, offset
));
420 case ILayoutNodeList::SNode::STYLE_DELETED
:
421 case ILayoutNodeList::SNode::STYLE_RENAMED
:
422 DrawShadow (graphics
, noderect
, shadowColor
, TSVNOctangle
);
424 case ILayoutNodeList::SNode::STYLE_ADDED
:
425 DrawShadow(graphics
, noderect
, shadowColor
, TSVNRoundRect
);
427 case ILayoutNodeList::SNode::STYLE_LAST
:
428 DrawShadow(graphics
, noderect
, shadowColor
, TSVNEllipse
);
431 DrawShadow(graphics
, noderect
, shadowColor
, TSVNRectangle
);
439 void CRevisionGraphWnd::DrawSquare
440 ( GraphicsDevice
& graphics
441 , const PointF
& leftTop
442 , const Color
& lightColor
443 , const Color
& darkColor
444 , const Color
& penColor
)
446 float squareSize
= MARKER_SIZE
* m_fZoomFactor
;
448 PointF
leftBottom (leftTop
.X
, leftTop
.Y
+ squareSize
);
449 RectF
square (leftTop
, SizeF (squareSize
, squareSize
));
451 if (graphics
.graphics
)
453 LinearGradientBrush
lgBrush (leftTop
, leftBottom
, lightColor
, darkColor
);
454 graphics
.graphics
->FillRectangle (&lgBrush
, square
);
455 if (squareSize
> 4.0f
)
458 graphics
.graphics
->DrawRectangle (&pen
, square
);
461 else if (graphics
.pSVG
)
463 graphics
.pSVG
->GradientRectangle((int)square
.X
, (int)square
.Y
, (int)square
.Width
, (int)square
.Height
,
464 lightColor
, darkColor
, penColor
);
469 void CRevisionGraphWnd::DrawGlyph
470 ( GraphicsDevice
& graphics
472 , const PointF
& leftTop
474 , GlyphPosition position
)
478 if (glyph
== NoGlyph
)
481 // bitmap source area
483 REAL x
= ((REAL
)position
+ (REAL
)glyph
) * GLYPH_BITMAP_SIZE
;
485 // screen target area
487 float glyphSize
= GLYPH_SIZE
* m_fZoomFactor
;
488 RectF
target (leftTop
, SizeF (glyphSize
, glyphSize
));
492 if (graphics
.graphics
)
494 graphics
.graphics
->DrawImage ( glyphs
496 , x
, 0.0f
, GLYPH_BITMAP_SIZE
, GLYPH_BITMAP_SIZE
497 , UnitPixel
, nullptr, nullptr, nullptr);
499 else if (graphics
.pSVG
)
501 // instead of inserting a bitmap, draw a
502 // round rectangle instead.
503 // Embedding images would blow up the resulting
504 // svg file a lot, and the round rectangle
507 // images could be embedded like this:
508 // <image y="100" x="100" id="imgId1234" xlink:href="data:image/png;base64,...base64endodeddata..." height="16" width="16" />
510 graphics
.pSVG
->RoundedRectangle((int)target
.X
, (int)target
.Y
, (int)target
.Width
, (int)target
.Height
,
511 Color(0,0,0), 2, Color(255,255,255), (int)(target
.Width
/3.0));
517 void CRevisionGraphWnd::DrawGlyphs
518 ( GraphicsDevice
& graphics
520 , const CVisibleGraphNode
* node
521 , const PointF
& center
524 , GlyphPosition position
529 // don't show collapse and cut glyths by default
531 if (!showAll
&& ((glyph1
== CollapseGlyph
) || (glyph1
== SplitGlyph
)))
533 if (!showAll
&& ((glyph2
== CollapseGlyph
) || (glyph2
== SplitGlyph
)))
536 // glyth2 shall be set only if 2 glyphs are in use
538 if (glyph1
== NoGlyph
)
540 std::swap (glyph1
, glyph2
);
541 std::swap (state1
, state2
);
546 if (glyph1
== NoGlyph
)
551 CSyncPointer
<CRevisionGraphState::TVisibleGlyphs
>
552 visibleGlyphs (m_state
.GetVisibleGlyphs());
554 float squareSize
= GLYPH_SIZE
* m_fZoomFactor
;
555 if (glyph2
== NoGlyph
)
557 PointF
leftTop (center
.X
- 0.5f
* squareSize
, center
.Y
- 0.5f
* squareSize
);
558 DrawGlyph (graphics
, glyphs
, leftTop
, glyph1
, position
);
559 visibleGlyphs
->push_back
560 (CRevisionGraphState::SVisibleGlyph (state1
, leftTop
, node
));
564 PointF
leftTop1 (center
.X
- squareSize
, center
.Y
- 0.5f
* squareSize
);
565 DrawGlyph (graphics
, glyphs
, leftTop1
, glyph1
, position
);
566 visibleGlyphs
->push_back
567 (CRevisionGraphState::SVisibleGlyph (state1
, leftTop1
, node
));
569 PointF
leftTop2 (center
.X
, center
.Y
- 0.5f
* squareSize
);
570 DrawGlyph (graphics
, glyphs
, leftTop2
, glyph2
, position
);
571 visibleGlyphs
->push_back
572 (CRevisionGraphState::SVisibleGlyph (state2
, leftTop2
, node
));
578 void CRevisionGraphWnd::DrawGlyphs
579 ( GraphicsDevice
& graphics
581 , const CVisibleGraphNode
* node
582 , const RectF
& nodeRect
589 if ((state
== 0) && (allowed
== 0))
594 PointF
topCenter (0.5f
* nodeRect
.GetLeft() + 0.5f
* nodeRect
.GetRight(), nodeRect
.GetTop());
595 PointF
rightCenter (nodeRect
.GetRight(), 0.5f
* nodeRect
.GetTop() + 0.5f
* nodeRect
.GetBottom());
596 PointF
bottomCenter (0.5f
* nodeRect
.GetLeft() + 0.5f
* nodeRect
.GetRight(), nodeRect
.GetBottom());
598 DrawGlyphs ( graphics
601 , upsideDown
? bottomCenter
: topCenter
602 , (state
& CGraphNodeStates::COLLAPSED_ABOVE
) ? ExpandGlyph
: CollapseGlyph
603 , (state
& CGraphNodeStates::SPLIT_ABOVE
) ? JoinGlyph
: SplitGlyph
604 , upsideDown
? Below
: Above
605 , CGraphNodeStates::COLLAPSED_ABOVE
606 , CGraphNodeStates::SPLIT_ABOVE
607 , (allowed
& CGraphNodeStates::COLLAPSED_ABOVE
) != 0);
609 DrawGlyphs ( graphics
613 , (state
& CGraphNodeStates::COLLAPSED_RIGHT
) ? ExpandGlyph
: CollapseGlyph
614 , (state
& CGraphNodeStates::SPLIT_RIGHT
) ? JoinGlyph
: SplitGlyph
616 , CGraphNodeStates::COLLAPSED_RIGHT
617 , CGraphNodeStates::SPLIT_RIGHT
618 , (allowed
& CGraphNodeStates::COLLAPSED_RIGHT
) != 0);
620 DrawGlyphs ( graphics
623 , upsideDown
? topCenter
: bottomCenter
624 , (state
& CGraphNodeStates::COLLAPSED_BELOW
) ? ExpandGlyph
: CollapseGlyph
625 , (state
& CGraphNodeStates::SPLIT_BELOW
) ? JoinGlyph
: SplitGlyph
626 , upsideDown
? Above
: Below
627 , CGraphNodeStates::COLLAPSED_BELOW
628 , CGraphNodeStates::SPLIT_BELOW
629 , (allowed
& CGraphNodeStates::COLLAPSED_BELOW
) != 0);
634 void CRevisionGraphWnd::IndicateGlyphDirection
635 ( GraphicsDevice
& graphics
636 , const ILayoutNodeList
* nodeList
637 , const ILayoutNodeList::SNode
& node
638 , const RectF
& nodeRect
641 , const CSize
& offset
)
648 // where to place the indication?
650 bool indicateAbove
= (glyphs
& CGraphNodeStates::COLLAPSED_ABOVE
) != 0;
651 bool indicateRight
= (glyphs
& CGraphNodeStates::COLLAPSED_RIGHT
) != 0;
652 bool indicateBelow
= (glyphs
& CGraphNodeStates::COLLAPSED_BELOW
) != 0;
654 // fill indication area a semi-transparent blend of red
655 // and the background color
658 color
.SetFromCOLORREF (GetSysColor(COLOR_WINDOW
));
659 color
.SetValue ((color
.GetValue() & 0x807f7f7f) + 0x800000);
661 SolidBrush
brush (color
);
663 // draw the indication (only one condition should match)
665 RectF glyphCenter
= (indicateAbove
^ upsideDown
)
666 ? RectF (nodeRect
.X
, nodeRect
.Y
- 1.0f
, 0.0f
, 0.0f
)
667 : RectF (nodeRect
.X
, nodeRect
.GetBottom() - 1.0f
, 0.0f
, 0.0f
);
671 const CVisibleGraphNode
* firstAffected
= node
.node
->GetSource();
673 assert (firstAffected
);
675 = GetBranchCover (nodeList
, firstAffected
->GetIndex(), true, offset
);
676 RectF::Union (branchCover
, branchCover
, glyphCenter
);
678 if (graphics
.graphics
)
679 graphics
.graphics
->FillRectangle (&brush
, branchCover
);
680 else if (graphics
.pSVG
)
681 graphics
.pSVG
->RoundedRectangle((int)branchCover
.X
, (int)branchCover
.Y
, (int)branchCover
.Width
, (int)branchCover
.Height
,
687 for ( const CVisibleGraphNode::CCopyTarget
* branch
688 = node
.node
->GetFirstCopyTarget()
690 ; branch
= branch
->next())
693 = GetBranchCover (nodeList
, branch
->value()->GetIndex(), false, offset
);
694 if (graphics
.graphics
)
695 graphics
.graphics
->FillRectangle (&brush
, branchCover
);
696 else if (graphics
.pSVG
)
697 graphics
.pSVG
->RoundedRectangle((int)branchCover
.X
, (int)branchCover
.Y
, (int)branchCover
.Width
, (int)branchCover
.Height
,
704 const CVisibleGraphNode
* firstAffected
705 = node
.node
->GetNext();
708 = GetBranchCover (nodeList
, firstAffected
->GetIndex(), false, offset
);
709 RectF::Union (branchCover
, branchCover
, glyphCenter
);
711 if (graphics
.graphics
)
712 graphics
.graphics
->FillRectangle (&brush
, branchCover
);
713 else if (graphics
.pSVG
)
714 graphics
.pSVG
->RoundedRectangle((int)branchCover
.X
, (int)branchCover
.Y
, (int)branchCover
.Width
, (int)branchCover
.Height
,
721 void CRevisionGraphWnd::DrawMarker
722 ( GraphicsDevice
& graphics
723 , const RectF
& noderect
724 , MarkerPosition
/*position*/
725 , int /*relPosition*/
726 , const Color
& penColor
729 REAL width
= 4*this->m_fZoomFactor
<1? 1: 4*this->m_fZoomFactor
;
730 Pen
pen(penColor
,width
);
731 DrawRoundedRect(graphics
, penColor
, (int)width
, &pen
, Color(0,0,0), nullptr, noderect
);
735 REAL x
= max(1, 10 * this->m_fZoomFactor
);
736 REAL y1
= max(1, 25 * this->m_fZoomFactor
);
737 REAL y2
= max(1, 5 * this->m_fZoomFactor
);
738 if(graphics
.graphics
)
740 graphics
.graphics
->DrawLine(&pen
, noderect
.X
+ x
, noderect
.Y
- y1
, noderect
.X
+ x
, noderect
.Y
- y2
);
741 if (m_SelectedEntry2
)
744 base
.AppendFormat(IDS_PROC_DIFF_BASE
);
746 SolidBrush
blackbrush(penColor
);
747 Gdiplus::Font
font(CAppUtils::GetLogFontName(), (REAL
)m_nFontSize
, FontStyleRegular
);
748 graphics
.graphics
->DrawString(base
, base
.GetLength(), &font
, Gdiplus::PointF(noderect
.X
+ x
+ width
, noderect
.Y
- y1
), &blackbrush
);
755 REAL x1
= max(1, 5 * this->m_fZoomFactor
);
756 REAL x2
= max(1, 15 * this->m_fZoomFactor
);
757 REAL y1
= max(1, 25 * this->m_fZoomFactor
);
758 REAL y2
= max(1, 5 * this->m_fZoomFactor
);
759 if(graphics
.graphics
)
761 graphics
.graphics
->DrawLine(&pen
, noderect
.X
+ x1
, noderect
.Y
- y1
, noderect
.X
+ x1
, noderect
.Y
- y2
);
762 graphics
.graphics
->DrawLine(&pen
, noderect
.X
+ x2
, noderect
.Y
- y1
, noderect
.X
+ x2
, noderect
.Y
- y2
);
768 void CRevisionGraphWnd::DrawStripes (GraphicsDevice
& graphics
, const CSize
& offset
)
770 // we need to fill this visible area of the the screen
771 // (even if there is graph in that part)
774 if (graphics
.graphics
)
775 graphics
.graphics
->GetVisibleClipBounds (&clipRect
);
777 // don't show stripes if we don't have multiple roots
779 CSyncPointer
<const ILayoutRectList
> trees (m_state
.GetTrees());
780 if (trees
->GetCount() < 2)
783 // iterate over all trees
785 for ( index_t i
= 0, count
= trees
->GetCount(); i
< count
; ++i
)
787 // screen coordinates covered by the tree
789 CRect tree
= trees
->GetRect(i
);
790 REAL left
= tree
.left
* m_fZoomFactor
;
791 REAL right
= tree
.right
* m_fZoomFactor
;
792 RectF
rect ( left
- offset
.cx
794 , i
+1 == count
? clipRect
.Width
: right
- left
799 if (rect
.IntersectsWith (clipRect
))
801 // draw the background stripe
803 Color
color ( (i
& 1) == 0
804 ? m_Colors
.GetColor (CColors::gdpStripeColor1
)
805 : m_Colors
.GetColor (CColors::gdpStripeColor2
));
806 if (graphics
.graphics
)
808 SolidBrush
brush (color
);
809 graphics
.graphics
->FillRectangle (&brush
, rect
);
811 else if (graphics
.pSVG
)
812 graphics
.pSVG
->RoundedRectangle((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
,
819 PointF
CRevisionGraphWnd::cutPoint(ogdf::node v
, double lw
, PointF ps
, PointF pt
)
821 double x
= m_GraphAttr
.x(v
);
822 double y
= m_GraphAttr
.y(v
);
823 double xmin
= x
- this->m_GraphAttr
.width(v
)/2 - lw
/2;
824 double xmax
= x
+ this->m_GraphAttr
.width(v
)/2 + lw
/2;
825 double ymin
= y
- this->m_GraphAttr
.height(v
)/2 - lw
/2;
826 double ymax
= y
+ this->m_GraphAttr
.height(v
)/2 + lw
/2;;
828 double dx
= pt
.X
- ps
.X
;
829 double dy
= pt
.Y
- ps
.Y
;
834 double t
= (ymax
-ps
.Y
) / dy
;
837 if(xmin
<= x
&& x
<= xmax
)
838 return PointF((REAL
)x
, (REAL
)ymax
);
841 } else if(pt
.Y
< ymin
) {
842 double t
= (ymin
-ps
.Y
) / dy
;
845 if(xmin
<= x
&& x
<= xmax
)
846 return PointF((REAL
)x
, (REAL
)ymin
);
853 double t
= (xmax
-ps
.X
) / dx
;
856 if(ymin
<= y
&& y
<= ymax
)
857 return PointF((REAL
)xmax
, (REAL
)y
);
860 } else if(pt
.X
< xmin
) {
861 double t
= (xmin
-ps
.X
) / dx
;
864 if(ymin
<= y
&& y
<= ymax
)
865 return PointF((REAL
)xmin
, (REAL
)y
);
872 void CRevisionGraphWnd::DrawConnections (GraphicsDevice
& graphics
, const CRect
& /*logRect*/, const CSize
& offset
)
874 CArray
<PointF
> points
;
877 if(graphics
.graphics
)
878 graphics
.graphics
->SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias
);
880 float penwidth
= 2*m_fZoomFactor
<1? 1:2*m_fZoomFactor
;
881 Gdiplus::Pen
pen(GetColorFromSysColor(COLOR_WINDOWTEXT
), penwidth
);
883 // iterate over all visible lines
885 forall_edges(e
, m_Graph
)
887 // get connection and point position
888 const auto& dpl
= this->m_GraphAttr
.bends(e
);
894 pt
.X
= (REAL
)m_GraphAttr
.x(e
->source());
895 pt
.Y
= (REAL
)m_GraphAttr
.y(e
->source());
899 for (auto it
= dpl
.begin(); it
.valid(); ++it
)
901 pt
.X
= (REAL
)(*it
).m_x
;
902 pt
.Y
= (REAL
)(*it
).m_y
;
906 pt
.X
= (REAL
)m_GraphAttr
.x(e
->target());
907 pt
.Y
= (REAL
)m_GraphAttr
.y(e
->target());
911 points
[0] = this->cutPoint(e
->source(), 1, points
[0], points
[1]);
912 points
[points
.GetCount()-1] = this->cutPoint(e
->target(), 1, points
[points
.GetCount()-1], points
[points
.GetCount()-2]);
913 // draw the connection
915 for (int i
= 0; i
< points
.GetCount(); ++i
)
918 points
[i
].X
= points
[i
].X
* this->m_fZoomFactor
- offset
.cx
;
919 points
[i
].Y
= points
[i
].Y
* this->m_fZoomFactor
- offset
.cy
;
923 if (graphics
.graphics
)
924 graphics
.graphics
->DrawLines(&pen
, points
.GetData(), (INT
)points
.GetCount());
925 else if (graphics
.pSVG
)
926 graphics
.pSVG
->Polyline(points
.GetData(), (int)points
.GetCount(), Color(0,0,0), (int)penwidth
);
927 else if (graphics
.pGraphviz
)
929 CString hash1
= L
'g' + m_logEntries
[e
->target()->index()].ToString().Left(g_Git
.GetShortHASHLength());
930 CString hash2
= L
'g' + m_logEntries
[e
->source()->index()].ToString().Left(g_Git
.GetShortHASHLength());
931 graphics
.pGraphviz
->DrawEdge(hash1
, hash2
);
935 double dx
= points
[1].X
- points
[0].X
;
936 double dy
= points
[1].Y
- points
[0].Y
;
938 double len
= sqrt(dx
*dx
+ dy
*dy
);
939 dx
= m_ArrowSize
* m_fZoomFactor
*dx
/len
;
940 dy
= m_ArrowSize
* m_fZoomFactor
*dy
/len
;
942 double p1_x
, p1_y
, p2_x
, p2_y
;
943 p1_x
= dx
* m_ArrowCos
- dy
* m_ArrowSin
;
944 p1_y
= dx
* m_ArrowSin
+ dy
* m_ArrowCos
;
946 p2_x
= dx
* m_ArrowCos
+ dy
* m_ArrowSin
;
947 p2_y
= -dx
* m_ArrowSin
+ dy
* m_ArrowCos
;
949 //graphics.graphics->DrawLine(&pen, points[0].X,points[0].Y, points[0].X +p1_x,points[0].Y+p1_y);
950 //graphics.graphics->DrawLine(&pen, points[0].X,points[0].Y, points[0].X +p2_x,points[0].Y+p2_y);
954 arrows
[0].X
= points
[0].X
;
955 arrows
[0].Y
= points
[0].Y
;
957 arrows
[1].X
= points
[0].X
+ (REAL
)p1_x
;
958 arrows
[1].Y
= points
[0].Y
+ (REAL
)p1_y
;
960 arrows
[2].X
= points
[0].X
+ (REAL
)dx
*3/5;
961 arrows
[2].Y
= points
[0].Y
+ (REAL
)dy
*3/5;
963 arrows
[3].X
= points
[0].X
+ (REAL
)p2_x
;
964 arrows
[3].Y
= points
[0].Y
+ (REAL
)p2_y
;
966 arrows
[4].X
= points
[0].X
;
967 arrows
[4].Y
= points
[0].Y
;
969 path
.AddLines(arrows
, 5);
970 path
.SetFillMode(FillModeAlternate
);
971 if(graphics
.graphics
)
972 graphics
.graphics
->DrawPath(&pen
, &path
);
973 else if(graphics
.pSVG
)
974 graphics
.pSVG
->DrawPath(arrows
, 5, Color(0,0,0), (int)penwidth
, Color(0,0,0));
978 void CRevisionGraphWnd::DrawTexts (GraphicsDevice
& graphics
, const CRect
& /*logRect*/, const CSize
& offset
)
980 //COLORREF standardTextColor = GetSysColor(COLOR_WINDOWTEXT);
981 if (m_nFontSize
<= 0)
984 // iterate over all visible nodes
987 graphics
.pDC
->SetTextAlign (TA_CENTER
| TA_TOP
);
989 CString fontname
= CAppUtils::GetLogFontName();
991 Gdiplus::Font
font(fontname
, (REAL
)m_nFontSize
, FontStyleRegular
);
992 SolidBrush
blackbrush((ARGB
)Color::Black
);
994 DWORD revGraphUseLocalForCur
= CRegDWORD(L
"Software\\TortoiseGit\\TortoiseProc\\Graph\\RevGraphUseLocalForCur");
997 forall_nodes(v
,m_Graph
)
999 // get node and position
1000 RectF
noderect (GetNodeRect (v
, offset
));
1002 // draw the revision text
1003 CGitHash hash
= this->m_logEntries
[v
->index()];
1004 double hight
= noderect
.Height
/ (!m_HashMap
[hash
].empty() ? m_HashMap
[hash
].size() : 1);
1006 if (m_HashMap
.find(hash
) == m_HashMap
.end() || m_HashMap
[hash
].empty())
1009 background
.SetFromCOLORREF (GetSysColor(COLOR_WINDOW
));
1010 Gdiplus::Pen
pen(background
,1.0F
);
1011 Color brightColor
= LimitedScaleColor (background
, RGB(255,0,0), 0.9f
);
1012 Gdiplus::SolidBrush
brush(brightColor
);
1014 DrawRoundedRect(graphics
, background
,1, &pen
, brightColor
, &brush
, noderect
);
1016 if(graphics
.graphics
)
1018 graphics
.graphics
->DrawString(hash
.ToString().Left(g_Git
.GetShortHASHLength()),-1,
1020 Gdiplus::PointF(noderect
.X
+ this->GetLeftRightMargin()*this->m_fZoomFactor
,noderect
.Y
+this->GetTopBottomMargin()*m_fZoomFactor
),
1025 graphics
.pSVG
->Text((int)(noderect
.X
+ this->GetLeftRightMargin() * this->m_fZoomFactor
),
1026 (int)(noderect
.Y
+ this->GetTopBottomMargin() * m_fZoomFactor
+ m_nFontSize
),
1027 CUnicodeUtils::GetUTF8(fontname
), m_nFontSize
, false, false, (ARGB
)Color::Black
,
1028 CUnicodeUtils::GetUTF8(hash
.ToString().Left(g_Git
.GetShortHASHLength())));
1030 if (graphics
.pGraphviz
)
1032 CString shortHash
= hash
.ToString().Left(g_Git
.GetShortHASHLength());
1033 graphics
.pGraphviz
->DrawNode(L
'g' + shortHash
, shortHash
, fontname
, m_nFontSize
, background
, brightColor
, (int)noderect
.Height
);
1037 if (graphics
.pGraphviz
)
1039 CString id
= L
'g' + hash
.ToString().Left(g_Git
.GetShortHASHLength());
1040 graphics
.pGraphviz
->BeginDrawTableNode(id
, fontname
, m_nFontSize
, (int)noderect
.Height
);
1043 for (size_t i
= 0; i
< m_HashMap
[hash
].size(); ++i
)
1046 CString str
= m_HashMap
[hash
][i
];
1049 rect
.X
= (REAL
)noderect
.X
;
1050 rect
.Y
= (REAL
)(noderect
.Y
+ hight
*i
);
1051 rect
.Width
= (REAL
)noderect
.Width
;
1052 rect
.Height
= (REAL
)hight
;
1054 COLORREF colRef
= m_Colors
.GetColor(CColors::OtherRef
);
1056 CGit::REF_TYPE refType
;
1057 shortname
= CGit::GetShortName(str
, &refType
);
1060 case CGit::REF_TYPE::LOCAL_BRANCH
:
1061 if (!revGraphUseLocalForCur
&& shortname
== m_CurrentBranch
)
1062 colRef
= m_Colors
.GetColor(CColors::CurrentBranch
);
1064 colRef
= m_Colors
.GetColor(CColors::LocalBranch
);
1066 case CGit::REF_TYPE::REMOTE_BRANCH
:
1067 colRef
= m_Colors
.GetColor(CColors::RemoteBranch
);
1069 case CGit::REF_TYPE::ANNOTATED_TAG
:
1070 case CGit::REF_TYPE::TAG
:
1071 colRef
= m_Colors
.GetColor(CColors::Tag
);
1073 case CGit::REF_TYPE::STASH
:
1074 colRef
= m_Colors
.GetColor(CColors::Stash
);
1076 case CGit::REF_TYPE::BISECT_GOOD
:
1077 colRef
= m_Colors
.GetColor(CColors::BisectGood
);
1079 case CGit::REF_TYPE::BISECT_BAD
:
1080 colRef
= m_Colors
.GetColor(CColors::BisectBad
);
1082 case CGit::REF_TYPE::BISECT_SKIP
:
1083 colRef
= m_Colors
.GetColor(CColors::BisectSkip
);
1085 case CGit::REF_TYPE::NOTES
:
1086 colRef
= m_Colors
.GetColor(CColors::NoteNode
);
1090 Gdiplus::Color
color(GetRValue(colRef
), GetGValue(colRef
), GetBValue(colRef
));
1091 Gdiplus::Pen
pen(color
);
1092 Gdiplus::SolidBrush
brush(color
);
1095 mask
|= (i
==0)? ROUND_UP
:0;
1096 mask
|= (i
== m_HashMap
[hash
].size()-1)? ROUND_DOWN
:0;
1097 this->DrawRoundedRect(graphics
, color
,1,&pen
, color
,&brush
, rect
,mask
);
1099 if (graphics
.graphics
)
1101 //graphics.graphics->FillRectangle(&SolidBrush(Gdiplus::Color(GetRValue(colRef), GetGValue(colRef), GetBValue(colRef))),
1104 graphics
.graphics
->DrawString(shortname
, shortname
.GetLength(),
1106 Gdiplus::PointF((REAL
)(noderect
.X
+ this->GetLeftRightMargin()*m_fZoomFactor
),
1107 (REAL
)(noderect
.Y
+ this->GetTopBottomMargin()*m_fZoomFactor
+ hight
*i
)),
1110 //graphics.graphics->DrawString(shortname.GetBuffer(), shortname.GetLength(), ::new Gdiplus::Font(graphics.pDC->m_hDC), PointF(noderect.X, noderect.Y + hight * i), nullptr, nullptr);
1113 else if (graphics
.pSVG
)
1114 graphics
.pSVG
->Text((int)(noderect
.X
+ this->GetLeftRightMargin() * m_fZoomFactor
),
1115 (int)(noderect
.Y
+ this->GetTopBottomMargin() * m_fZoomFactor
+ hight
* i
+ m_nFontSize
),
1116 CUnicodeUtils::GetUTF8(fontname
), m_nFontSize
,
1117 false, false, (ARGB
)Color::Black
, CUnicodeUtils::GetUTF8(shortname
));
1118 else if (graphics
.pGraphviz
)
1119 graphics
.pGraphviz
->DrawTableNode(shortname
, color
);
1122 if (graphics
.pGraphviz
)
1123 graphics
.pGraphviz
->EndDrawTableNode();
1125 if ((m_SelectedEntry1
== v
))
1126 DrawMarker(graphics
, noderect
, mpLeft
, 0, GetColorFromSysColor(COLOR_HIGHLIGHT
), 1);
1128 if ((m_SelectedEntry2
== v
))
1129 DrawMarker(graphics
, noderect
, mpLeft
, 0, Color(136,0, 21), 2);
1135 void CRevisionGraphWnd::DrawCurrentNodeGlyphs (GraphicsDevice
& graphics
, Image
* glyphs
, const CSize
& offset
)
1137 CSyncPointer
<const ILayoutNodeList
> nodeList (m_state
.GetNodes());
1139 = m_state
.GetOptions()->GetOption
<CUpsideDownLayout
>()->IsActive();
1141 // don't draw glyphs if we are outside the client area
1142 // (e.g. within a scrollbar)
1145 GetCursorPos (&point
);
1146 ScreenToClient (&point
);
1147 if (!GetClientRect().PtInRect (point
))
1150 // expansion glypths etc.
1152 m_hoverIndex
= GetHitNode (point
);
1153 m_hoverGlyphs
= GetHoverGlyphs (point
);
1155 if ((m_hoverIndex
!= NO_INDEX
) || (m_hoverGlyphs
!= 0))
1157 index_t nodeIndex
= m_hoverIndex
== NO_INDEX
1158 ? GetHitNode (point
, CSize (GLYPH_SIZE
, GLYPH_SIZE
/ 2))
1161 if (nodeIndex
>= nodeList
->GetCount())
1164 ILayoutNodeList::SNode node
= nodeList
->GetNode (nodeIndex
);
1165 RectF
noderect (GetNodeRect (node
, offset
));
1167 DWORD flags
= m_state
.GetNodeStates()->GetFlags (node
.node
);
1169 IndicateGlyphDirection (graphics
, nodeList
.get(), node
, noderect
, m_hoverGlyphs
, upsideDown
, offset
);
1170 DrawGlyphs (graphics
, glyphs
, node
.node
, noderect
, flags
, m_hoverGlyphs
, upsideDown
);
1175 void CRevisionGraphWnd::DrawGraph(GraphicsDevice
& graphics
, const CRect
& rect
, int nVScrollPos
, int nHScrollPos
, bool bDirectDraw
)
1177 CMemDC
* memDC
= nullptr;
1182 memDC
= new CMemDC (*graphics
.pDC
, rect
);
1183 graphics
.pDC
= &memDC
->GetDC();
1186 graphics
.pDC
->FillSolidRect(rect
, GetSysColor(COLOR_WINDOW
));
1187 graphics
.pDC
->SetBkMode(TRANSPARENT
);
1190 // preparation & sync
1192 //CSyncPointer<CAllRevisionGraphOptions> options (m_state.GetOptions());
1193 ClearVisibleGlyphs (rect
);
1195 // transform visible
1197 CSize
offset (nHScrollPos
, nVScrollPos
);
1198 CRect
logRect ( (int)(offset
.cx
/ m_fZoomFactor
)-1
1199 , (int)(offset
.cy
/ m_fZoomFactor
)-1
1200 , (int)((rect
.Width() + offset
.cx
) / m_fZoomFactor
) + 1
1201 , (int)((rect
.Height() + offset
.cy
) / m_fZoomFactor
) + 1);
1203 // draw the different components
1207 Graphics
* gcs
= Graphics::FromHDC(*graphics
.pDC
);
1208 graphics
.graphics
= gcs
;
1209 gcs
->SetPageUnit (UnitPixel
);
1210 gcs
->SetInterpolationMode (InterpolationModeHighQualityBicubic
);
1211 gcs
->SetSmoothingMode(SmoothingModeAntiAlias
);
1212 gcs
->SetClip(RectF(Gdiplus::REAL(rect
.left
), Gdiplus::REAL(rect
.top
), Gdiplus::REAL(rect
.Width()), Gdiplus::REAL(rect
.Height())));
1215 // if (options->GetOption<CShowTreeStripes>()->IsActive())
1216 // DrawStripes (graphics, offset);
1218 //if (m_fZoomFactor > SHADOW_ZOOM_THRESHOLD)
1219 // DrawShadows (graphics, logRect, offset);
1221 Bitmap
glyphs (AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_REVGRAPHGLYPHS
));
1223 DrawTexts (graphics
, logRect
, offset
);
1224 DrawConnections (graphics
, logRect
, offset
);
1225 //if (m_showHoverGlyphs)
1226 // DrawCurrentNodeGlyphs (graphics, &glyphs, offset);
1230 if ((!bDirectDraw
)&&(m_Preview
.GetSafeHandle())&&(m_bShowOverview
)&&(graphics
.pDC
))
1232 // draw the overview image rectangle in the top right corner
1233 CMyMemDC
memDC2(graphics
.pDC
, true);
1234 memDC2
.SetWindowOrg(0, 0);
1235 HBITMAP oldhbm
= (HBITMAP
)memDC2
.SelectObject(&m_Preview
);
1236 graphics
.pDC
->BitBlt(rect
.Width()-m_previewWidth
, rect
.Height() - m_previewHeight
, m_previewWidth
, m_previewHeight
,
1237 &memDC2
, 0, 0, SRCCOPY
);
1238 memDC2
.SelectObject(oldhbm
);
1239 // draw the border for the overview rectangle
1240 m_OverviewRect
.left
= rect
.Width()-m_previewWidth
;
1241 m_OverviewRect
.top
= rect
.Height()- m_previewHeight
;
1242 m_OverviewRect
.right
= rect
.Width();
1243 m_OverviewRect
.bottom
= rect
.Height();
1244 graphics
.pDC
->DrawEdge(&m_OverviewRect
, EDGE_BUMP
, BF_RECT
);
1245 // now draw a rectangle where the current view is located in the overview
1247 LONG width
= (long)(rect
.Width() * m_previewZoom
/ m_fZoomFactor
);
1248 LONG height
= (long)(rect
.Height() * m_previewZoom
/ m_fZoomFactor
);
1249 LONG xpos
= (long)(nHScrollPos
* m_previewZoom
/ m_fZoomFactor
);
1250 LONG ypos
= (long)(nVScrollPos
* m_previewZoom
/ m_fZoomFactor
);
1252 tempRect
.left
= rect
.Width()-m_previewWidth
+xpos
;
1253 tempRect
.top
= rect
.Height() - m_previewHeight
+ ypos
;
1254 tempRect
.right
= tempRect
.left
+ width
;
1255 tempRect
.bottom
= tempRect
.top
+ height
;
1256 // make sure the position rect is not bigger than the preview window itself
1257 ::IntersectRect(&m_OverviewPosRect
, &m_OverviewRect
, &tempRect
);
1259 RectF
rect2 ( (float)m_OverviewPosRect
.left
, (float)m_OverviewPosRect
.top
1260 , (float)m_OverviewPosRect
.Width(), (float)m_OverviewPosRect
.Height());
1261 if (graphics
.graphics
)
1263 SolidBrush
brush (Color (64, 0, 0, 0));
1264 graphics
.graphics
->FillRectangle (&brush
, rect2
);
1265 graphics
.pDC
->DrawEdge(&m_OverviewPosRect
, EDGE_BUMP
, BF_RECT
);
1269 // flush changes to screen
1271 delete graphics
.graphics
;
1275 void CRevisionGraphWnd::SetNodeRect(GraphicsDevice
& graphics
, ogdf::node
*pnode
, CGitHash rev
, int mode
)
1277 //multi - line mode. One RefName is one new line
1278 CString fontname
= CAppUtils::GetLogFontName();
1281 if(this->m_HashMap
.find(rev
) == m_HashMap
.end())
1283 CString shorthash
= rev
.ToString().Left(g_Git
.GetShortHASHLength());
1285 if(graphics
.graphics
)
1287 //GetTextExtentPoint32(graphics.pDC->m_hDC, shorthash.GetBuffer(), shorthash.GetLength(), &size);
1288 Gdiplus::Font
font(fontname
, (REAL
)m_nFontSize
, FontStyleRegular
);
1289 graphics
.graphics
->MeasureString(shorthash
, shorthash
.GetLength(),
1291 Gdiplus::PointF(0,0), &rect
);
1293 m_GraphAttr
.width(*pnode
) = this->GetLeftRightMargin()*2 + rect
.Width
;
1294 m_GraphAttr
.height(*pnode
) = this->GetTopBottomMargin()*2 + rect
.Height
;
1301 for (size_t i
= 0; i
< m_HashMap
[rev
].size(); ++i
)
1304 CString shortref
= m_HashMap
[rev
][i
];
1305 shortref
= CGit::GetShortName(shortref
, nullptr);
1308 Gdiplus::Font
font(fontname
, (REAL
)m_nFontSize
, FontStyleRegular
);
1309 graphics
.graphics
->MeasureString(shortref
, shortref
.GetLength(),
1311 Gdiplus::PointF(0,0), &rect
);
1312 if(rect
.Width
> xmax
)
1314 if(rect
.Height
> ymax
)
1319 m_GraphAttr
.width(*pnode
) = this->GetLeftRightMargin()*2 + xmax
;
1320 m_GraphAttr
.height(*pnode
) = (this->GetTopBottomMargin()*2 + ymax
) * lines
;