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
;
47 Color
GetColorFromSysColor(int nIndex
)
50 color
.SetFromCOLORREF(GetSysColor(nIndex
));
54 /************************************************************************/
55 /* Graphing functions */
56 /************************************************************************/
57 CFont
* CRevisionGraphWnd::GetFont(BOOL bItalic
/*= FALSE*/, BOOL bBold
/*= FALSE*/)
64 if (!m_apFonts
[nIndex
])
66 m_apFonts
[nIndex
] = new CFont
;
67 m_lfBaseFont
.lfWeight
= bBold
? FW_BOLD
: FW_NORMAL
;
68 m_lfBaseFont
.lfItalic
= (BYTE
) bItalic
;
69 m_lfBaseFont
.lfStrikeOut
= (BYTE
) FALSE
;
70 m_lfBaseFont
.lfHeight
= -CDPIAware::Instance().PointsToPixelsY(m_nFontSize
);
71 // use the empty font name, so GDI takes the first font which matches
72 // the specs. Maybe this will help render chinese/japanese chars correctly.
73 wcsncpy_s(m_lfBaseFont
.lfFaceName
, L
"MS Shell Dlg 2", _countof(m_lfBaseFont
.lfFaceName
) - 1);
74 if (!m_apFonts
[nIndex
]->CreateFontIndirect(&m_lfBaseFont
))
76 delete m_apFonts
[nIndex
];
77 m_apFonts
[nIndex
] = nullptr;
78 return CWnd::GetFont();
81 return m_apFonts
[nIndex
];
84 BOOL
CRevisionGraphWnd::OnEraseBkgnd(CDC
* /*pDC*/)
89 void CRevisionGraphWnd::OnPaint()
91 CPaintDC
dc(this); // device context for painting
92 CRect rect
= GetClientRect();
94 if (IsUpdateJobRunning())
96 CString fetch
= CString(MAKEINTRESOURCE(IDS_PROC_LOADING
));
97 dc
.FillSolidRect(rect
, ::GetSysColor(COLOR_APPWORKSPACE
));
98 dc
.ExtTextOut(20, 20, ETO_CLIPPED
, nullptr, fetch
, nullptr);
102 }else if (this->m_Graph
.empty())
104 CString sNoGraphText
;
105 sNoGraphText
.LoadString(IDS_REVGRAPH_ERR_NOGRAPH
);
106 dc
.FillSolidRect(rect
, RGB(255,255,255));
107 dc
.ExtTextOut(20, 20, ETO_CLIPPED
, nullptr, sNoGraphText
, nullptr);
113 DrawGraph(dev
, rect
, GetScrollPos(SB_VERT
), GetScrollPos(SB_HORZ
), false);
116 void CRevisionGraphWnd::ClearVisibleGlyphs (const CRect
& /*rect*/)
119 float glyphSize
= GLYPH_SIZE
* m_fZoomFactor
;
121 CSyncPointer
<CRevisionGraphState::TVisibleGlyphs
>
122 visibleGlyphs (m_state
.GetVisibleGlyphs());
124 for (size_t i
= visibleGlyphs
->size(), count
= i
; i
> 0; --i
)
126 const PointF
& leftTop
= (*visibleGlyphs
)[i
-1].leftTop
;
127 CRect
glyphRect ( static_cast<int>(leftTop
.X
)
128 , static_cast<int>(leftTop
.Y
)
129 , static_cast<int>(leftTop
.X
+ glyphSize
)
130 , static_cast<int>(leftTop
.Y
+ glyphSize
));
132 if (CRect().IntersectRect (glyphRect
, rect
))
134 (*visibleGlyphs
)[i
-1] = (*visibleGlyphs
)[--count
];
135 visibleGlyphs
->pop_back();
141 void CRevisionGraphWnd::CutawayPoints (const RectF
& rect
, float cutLen
, TCutRectangle
& result
)
143 result
[0] = PointF (rect
.X
, rect
.Y
+ cutLen
);
144 result
[1] = PointF (rect
.X
+ cutLen
, rect
.Y
);
145 result
[2] = PointF (rect
.GetRight() - cutLen
, rect
.Y
);
146 result
[3] = PointF (rect
.GetRight(), rect
.Y
+ cutLen
);
147 result
[4] = PointF (rect
.GetRight(), rect
.GetBottom() - cutLen
);
148 result
[5] = PointF (rect
.GetRight() - cutLen
, rect
.GetBottom());
149 result
[6] = PointF (rect
.X
+ cutLen
, rect
.GetBottom());
150 result
[7] = PointF (rect
.X
, rect
.GetBottom() - cutLen
);
153 void CRevisionGraphWnd::DrawRoundedRect (GraphicsDevice
& graphics
, const Color
& penColor
, int penWidth
, const Pen
* pen
, const Color
& fillColor
, const Brush
* brush
, const RectF
& rect
, int mask
)
155 enum {POINT_COUNT
= 8};
157 float radius
= CORNER_SIZE
* m_fZoomFactor
;
158 PointF points
[POINT_COUNT
];
159 CutawayPoints (rect
, radius
, points
);
161 if (graphics
.graphics
)
167 path
.AddArc (points
[0].X
, points
[1].Y
, radius
, radius
, 180, 90);
168 path
.AddArc (points
[2].X
, points
[2].Y
, radius
, radius
, 270, 90);
170 path
.AddLine(points
[0].X
, points
[1].Y
, points
[3].X
, points
[2].Y
);
172 if(mask
& ROUND_DOWN
)
174 path
.AddArc (points
[5].X
, points
[4].Y
, radius
, radius
, 0, 90);
175 path
.AddArc (points
[7].X
, points
[7].Y
, radius
, radius
, 90, 90);
178 path
.AddLine(points
[3].X
, points
[3].Y
, points
[4].X
, points
[5].Y
);
179 path
.AddLine(points
[4].X
, points
[5].Y
, points
[7].X
, points
[6].Y
);
182 points
[0].Y
-= radius
/ 2;
183 path
.AddLine (points
[7], points
[0]);
186 graphics
.graphics
->FillPath (brush
, &path
);
188 graphics
.graphics
->DrawPath (pen
, &path
);
190 else if (graphics
.pSVG
)
191 graphics
.pSVG
->RoundedRectangle((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
, penColor
, penWidth
, fillColor
, (int)radius
, mask
);
194 void CRevisionGraphWnd::DrawOctangle (GraphicsDevice
& graphics
, const Color
& penColor
, int penWidth
, const Pen
* pen
, const Color
& fillColor
, const Brush
* brush
, const RectF
& rect
)
196 enum {POINT_COUNT
= 8};
198 // show left & right edges of low boxes as "<===>"
200 float minCutAway
= min (CORNER_SIZE
* m_fZoomFactor
, rect
.Height
/ 2);
202 // larger boxes: remove 25% of the shorter side
204 float suggestedCutAway
= min (rect
.Height
, rect
.Width
) / 4;
206 // use the more visible one of the former two
208 PointF points
[POINT_COUNT
];
209 CutawayPoints (rect
, max (minCutAway
, suggestedCutAway
), points
);
213 if (graphics
.graphics
)
216 graphics
.graphics
->FillPolygon (brush
, points
, POINT_COUNT
);
218 graphics
.graphics
->DrawPolygon (pen
, points
, POINT_COUNT
);
220 else if (graphics
.pSVG
)
222 graphics
.pSVG
->Polygon(points
, POINT_COUNT
, penColor
, penWidth
, fillColor
);
228 void CRevisionGraphWnd::DrawShape (GraphicsDevice
& graphics
, const Color
& penColor
, int penWidth
, const Pen
* pen
, const Color
& fillColor
, const Brush
* brush
, const RectF
& rect
, NodeShape shape
)
233 if (graphics
.graphics
)
236 graphics
.graphics
->FillRectangle (brush
, rect
);
238 graphics
.graphics
->DrawRectangle (pen
, rect
);
240 else if (graphics
.pSVG
)
242 graphics
.pSVG
->RoundedRectangle((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
, penColor
, penWidth
, fillColor
);
246 DrawRoundedRect (graphics
, penColor
, penWidth
, pen
, fillColor
, brush
, rect
);
249 DrawOctangle (graphics
, penColor
, penWidth
, pen
, fillColor
, brush
, rect
);
252 if (graphics
.graphics
)
255 graphics
.graphics
->FillEllipse (brush
, rect
);
257 graphics
.graphics
->DrawEllipse(pen
, rect
);
259 else if (graphics
.pSVG
)
260 graphics
.pSVG
->Ellipse((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
, penColor
, penWidth
, fillColor
);
263 ASSERT(FALSE
); //unknown type
269 inline BYTE
LimitedScaleColor (BYTE c1
, BYTE c2
, float factor
)
271 BYTE scaled
= c2
+ (BYTE
)((c1
-c2
)*factor
);
277 Color
LimitedScaleColor (const Color
& c1
, const Color
& c2
, float factor
)
279 return Color ( LimitedScaleColor (c1
.GetA(), c2
.GetA(), factor
)
280 , LimitedScaleColor (c1
.GetR(), c2
.GetR(), factor
)
281 , LimitedScaleColor (c1
.GetG(), c2
.GetG(), factor
)
282 , LimitedScaleColor (c1
.GetB(), c2
.GetB(), factor
));
285 inline BYTE
Darken (BYTE c
)
289 : BYTE(int(2*c
) - 0x100);
292 Color
Darken (const Color
& c
)
297 , Darken (c
.GetB()));
300 BYTE
MaxComponentDiff (const Color
& c1
, const Color
& c2
)
302 int rDiff
= abs ((int)c1
.GetR() - (int)c2
.GetR());
303 int gDiff
= abs ((int)c1
.GetG() - (int)c2
.GetG());
304 int bDiff
= abs ((int)c1
.GetB() - (int)c2
.GetB());
306 return (BYTE
) max (max (rDiff
, gDiff
), bDiff
);
310 void CRevisionGraphWnd::DrawShadow (GraphicsDevice
& graphics
, const RectF
& rect
,
311 Color shadowColor
, NodeShape shape
)
316 shadow
.Offset (2, 2);
318 Pen
pen (shadowColor
);
319 SolidBrush
brush (shadowColor
);
321 DrawShape (graphics
, shadowColor
, 1, &pen
, shadowColor
, &brush
, shadow
, shape
);
325 RectF
CRevisionGraphWnd::TransformRectToScreen (const CRect
& rect
, const CSize
& offset
) const
327 PointF
leftTop ( rect
.left
* m_fZoomFactor
328 , rect
.top
* m_fZoomFactor
);
329 return RectF ( leftTop
.X
- offset
.cx
330 , leftTop
.Y
- offset
.cy
331 , rect
.right
* m_fZoomFactor
- leftTop
.X
- 1
332 , rect
.bottom
* m_fZoomFactor
- leftTop
.Y
);
336 RectF
CRevisionGraphWnd::GetNodeRect (const node
& node
, const CSize
& offset
) const
338 // get node and position
341 rect
.left
= (int) (this->m_GraphAttr
.x(node
) - m_GraphAttr
.width(node
)/2);
342 rect
.top
= (int) (this->m_GraphAttr
.y(node
) - m_GraphAttr
.height(node
)/2);
343 rect
.bottom
= (int)( rect
.top
+ m_GraphAttr
.height(node
));
344 rect
.right
= (int)(rect
.left
+ m_GraphAttr
.width(node
));
346 RectF
noderect (TransformRectToScreen (rect
, offset
));
348 // show two separate lines for touching nodes,
349 // unless the scale is too small
351 if (noderect
.Height
> 15.0f
)
352 noderect
.Height
-= 1.0f
;
362 isionGraphWnd::GetBranchCover
363 ( const ILayoutNodeList
* nodeList
366 , const CSize
& offset
)
368 // construct a rect that covers the respective branch
370 CRect
cover (0, 0, 0, 0);
371 while (nodeIndex
!= NO_INDEX
)
373 ILayoutNodeList::SNode node
= nodeList
->GetNode (nodeIndex
);
376 const CVisibleGraphNode
* nextNode
= upward
377 ? node
.node
->GetPrevious()
378 : node
.node
->GetNext();
380 nodeIndex
= !nextNode
? NO_INDEX
: nextNode
->GetIndex();
383 // expand it just a little to make it look nicer
385 cover
.InflateRect (10, 2, 10, 2);
387 // and now, transfrom it
389 return TransformRectToScreen (cover
, offset
);
394 void CRevisionGraphWnd::DrawShadows (GraphicsDevice
& graphics
, const CRect
& logRect
, const CSize
& offset
)
396 // shadow color to use
399 background
.SetFromCOLORREF (GetSysColor(COLOR_WINDOW
));
401 textColor
.SetFromCOLORREF (GetSysColor(COLOR_WINDOWTEXT
));
403 Color shadowColor
= LimitedScaleColor (background
, ARGB (Color::Black
), 0.5f
);
405 // iterate over all visible nodes
407 CSyncPointer
<const ILayoutNodeList
> nodes (m_state
.GetNodes());
408 for ( index_t index
= nodes
->GetFirstVisible (logRect
)
410 ; index
= nodes
->GetNextVisible (index
, logRect
))
412 // get node and position
414 ILayoutNodeList::SNode node
= nodes
->GetNode (index
);
415 RectF
noderect (GetNodeRect (node
, offset
));
421 case ILayoutNodeList::SNode::STYLE_DELETED
:
422 case ILayoutNodeList::SNode::STYLE_RENAMED
:
423 DrawShadow (graphics
, noderect
, shadowColor
, TSVNOctangle
);
425 case ILayoutNodeList::SNode::STYLE_ADDED
:
426 DrawShadow(graphics
, noderect
, shadowColor
, TSVNRoundRect
);
428 case ILayoutNodeList::SNode::STYLE_LAST
:
429 DrawShadow(graphics
, noderect
, shadowColor
, TSVNEllipse
);
432 DrawShadow(graphics
, noderect
, shadowColor
, TSVNRectangle
);
440 void CRevisionGraphWnd::DrawSquare
441 ( GraphicsDevice
& graphics
442 , const PointF
& leftTop
443 , const Color
& lightColor
444 , const Color
& darkColor
445 , const Color
& penColor
)
447 float squareSize
= MARKER_SIZE
* m_fZoomFactor
;
449 PointF
leftBottom (leftTop
.X
, leftTop
.Y
+ squareSize
);
450 RectF
square (leftTop
, SizeF (squareSize
, squareSize
));
452 if (graphics
.graphics
)
454 LinearGradientBrush
lgBrush (leftTop
, leftBottom
, lightColor
, darkColor
);
455 graphics
.graphics
->FillRectangle (&lgBrush
, square
);
456 if (squareSize
> 4.0f
)
459 graphics
.graphics
->DrawRectangle (&pen
, square
);
462 else if (graphics
.pSVG
)
464 graphics
.pSVG
->GradientRectangle((int)square
.X
, (int)square
.Y
, (int)square
.Width
, (int)square
.Height
,
465 lightColor
, darkColor
, penColor
);
470 void CRevisionGraphWnd::DrawGlyph
471 ( GraphicsDevice
& graphics
473 , const PointF
& leftTop
475 , GlyphPosition position
)
479 if (glyph
== NoGlyph
)
482 // bitmap source area
484 REAL x
= ((REAL
)position
+ (REAL
)glyph
) * GLYPH_BITMAP_SIZE
;
486 // screen target area
488 float glyphSize
= GLYPH_SIZE
* m_fZoomFactor
;
489 RectF
target (leftTop
, SizeF (glyphSize
, glyphSize
));
493 if (graphics
.graphics
)
495 graphics
.graphics
->DrawImage ( glyphs
497 , x
, 0.0f
, GLYPH_BITMAP_SIZE
, GLYPH_BITMAP_SIZE
498 , UnitPixel
, nullptr, nullptr, nullptr);
500 else if (graphics
.pSVG
)
502 // instead of inserting a bitmap, draw a
503 // round rectangle instead.
504 // Embedding images would blow up the resulting
505 // svg file a lot, and the round rectangle
508 // images could be embedded like this:
509 // <image y="100" x="100" id="imgId1234" xlink:href="data:image/png;base64,...base64endodeddata..." height="16" width="16" />
511 graphics
.pSVG
->RoundedRectangle((int)target
.X
, (int)target
.Y
, (int)target
.Width
, (int)target
.Height
,
512 Color(0,0,0), 2, Color(255,255,255), (int)(target
.Width
/3.0));
518 void CRevisionGraphWnd::DrawGlyphs
519 ( GraphicsDevice
& graphics
521 , const CVisibleGraphNode
* node
522 , const PointF
& center
525 , GlyphPosition position
530 // don't show collapse and cut glyths by default
532 if (!showAll
&& ((glyph1
== CollapseGlyph
) || (glyph1
== SplitGlyph
)))
534 if (!showAll
&& ((glyph2
== CollapseGlyph
) || (glyph2
== SplitGlyph
)))
537 // glyth2 shall be set only if 2 glyphs are in use
539 if (glyph1
== NoGlyph
)
541 std::swap (glyph1
, glyph2
);
542 std::swap (state1
, state2
);
547 if (glyph1
== NoGlyph
)
552 CSyncPointer
<CRevisionGraphState::TVisibleGlyphs
>
553 visibleGlyphs (m_state
.GetVisibleGlyphs());
555 float squareSize
= GLYPH_SIZE
* m_fZoomFactor
;
556 if (glyph2
== NoGlyph
)
558 PointF
leftTop (center
.X
- 0.5f
* squareSize
, center
.Y
- 0.5f
* squareSize
);
559 DrawGlyph (graphics
, glyphs
, leftTop
, glyph1
, position
);
560 visibleGlyphs
->push_back
561 (CRevisionGraphState::SVisibleGlyph (state1
, leftTop
, node
));
565 PointF
leftTop1 (center
.X
- squareSize
, center
.Y
- 0.5f
* squareSize
);
566 DrawGlyph (graphics
, glyphs
, leftTop1
, glyph1
, position
);
567 visibleGlyphs
->push_back
568 (CRevisionGraphState::SVisibleGlyph (state1
, leftTop1
, node
));
570 PointF
leftTop2 (center
.X
, center
.Y
- 0.5f
* squareSize
);
571 DrawGlyph (graphics
, glyphs
, leftTop2
, glyph2
, position
);
572 visibleGlyphs
->push_back
573 (CRevisionGraphState::SVisibleGlyph (state2
, leftTop2
, node
));
579 void CRevisionGraphWnd::DrawGlyphs
580 ( GraphicsDevice
& graphics
582 , const CVisibleGraphNode
* node
583 , const RectF
& nodeRect
590 if ((state
== 0) && (allowed
== 0))
595 PointF
topCenter (0.5f
* nodeRect
.GetLeft() + 0.5f
* nodeRect
.GetRight(), nodeRect
.GetTop());
596 PointF
rightCenter (nodeRect
.GetRight(), 0.5f
* nodeRect
.GetTop() + 0.5f
* nodeRect
.GetBottom());
597 PointF
bottomCenter (0.5f
* nodeRect
.GetLeft() + 0.5f
* nodeRect
.GetRight(), nodeRect
.GetBottom());
599 DrawGlyphs ( graphics
602 , upsideDown
? bottomCenter
: topCenter
603 , (state
& CGraphNodeStates::COLLAPSED_ABOVE
) ? ExpandGlyph
: CollapseGlyph
604 , (state
& CGraphNodeStates::SPLIT_ABOVE
) ? JoinGlyph
: SplitGlyph
605 , upsideDown
? Below
: Above
606 , CGraphNodeStates::COLLAPSED_ABOVE
607 , CGraphNodeStates::SPLIT_ABOVE
608 , (allowed
& CGraphNodeStates::COLLAPSED_ABOVE
) != 0);
610 DrawGlyphs ( graphics
614 , (state
& CGraphNodeStates::COLLAPSED_RIGHT
) ? ExpandGlyph
: CollapseGlyph
615 , (state
& CGraphNodeStates::SPLIT_RIGHT
) ? JoinGlyph
: SplitGlyph
617 , CGraphNodeStates::COLLAPSED_RIGHT
618 , CGraphNodeStates::SPLIT_RIGHT
619 , (allowed
& CGraphNodeStates::COLLAPSED_RIGHT
) != 0);
621 DrawGlyphs ( graphics
624 , upsideDown
? topCenter
: bottomCenter
625 , (state
& CGraphNodeStates::COLLAPSED_BELOW
) ? ExpandGlyph
: CollapseGlyph
626 , (state
& CGraphNodeStates::SPLIT_BELOW
) ? JoinGlyph
: SplitGlyph
627 , upsideDown
? Above
: Below
628 , CGraphNodeStates::COLLAPSED_BELOW
629 , CGraphNodeStates::SPLIT_BELOW
630 , (allowed
& CGraphNodeStates::COLLAPSED_BELOW
) != 0);
635 void CRevisionGraphWnd::IndicateGlyphDirection
636 ( GraphicsDevice
& graphics
637 , const ILayoutNodeList
* nodeList
638 , const ILayoutNodeList::SNode
& node
639 , const RectF
& nodeRect
642 , const CSize
& offset
)
649 // where to place the indication?
651 bool indicateAbove
= (glyphs
& CGraphNodeStates::COLLAPSED_ABOVE
) != 0;
652 bool indicateRight
= (glyphs
& CGraphNodeStates::COLLAPSED_RIGHT
) != 0;
653 bool indicateBelow
= (glyphs
& CGraphNodeStates::COLLAPSED_BELOW
) != 0;
655 // fill indication area a semi-transparent blend of red
656 // and the background color
659 color
.SetFromCOLORREF (GetSysColor(COLOR_WINDOW
));
660 color
.SetValue ((color
.GetValue() & 0x807f7f7f) + 0x800000);
662 SolidBrush
brush (color
);
664 // draw the indication (only one condition should match)
666 RectF glyphCenter
= (indicateAbove
^ upsideDown
)
667 ? RectF (nodeRect
.X
, nodeRect
.Y
- 1.0f
, 0.0f
, 0.0f
)
668 : RectF (nodeRect
.X
, nodeRect
.GetBottom() - 1.0f
, 0.0f
, 0.0f
);
672 const CVisibleGraphNode
* firstAffected
= node
.node
->GetSource();
674 assert (firstAffected
);
676 = GetBranchCover (nodeList
, firstAffected
->GetIndex(), true, offset
);
677 RectF::Union (branchCover
, branchCover
, glyphCenter
);
679 if (graphics
.graphics
)
680 graphics
.graphics
->FillRectangle (&brush
, branchCover
);
681 else if (graphics
.pSVG
)
682 graphics
.pSVG
->RoundedRectangle((int)branchCover
.X
, (int)branchCover
.Y
, (int)branchCover
.Width
, (int)branchCover
.Height
,
688 for ( const CVisibleGraphNode::CCopyTarget
* branch
689 = node
.node
->GetFirstCopyTarget()
691 ; branch
= branch
->next())
694 = GetBranchCover (nodeList
, branch
->value()->GetIndex(), false, offset
);
695 if (graphics
.graphics
)
696 graphics
.graphics
->FillRectangle (&brush
, branchCover
);
697 else if (graphics
.pSVG
)
698 graphics
.pSVG
->RoundedRectangle((int)branchCover
.X
, (int)branchCover
.Y
, (int)branchCover
.Width
, (int)branchCover
.Height
,
705 const CVisibleGraphNode
* firstAffected
706 = node
.node
->GetNext();
709 = GetBranchCover (nodeList
, firstAffected
->GetIndex(), false, offset
);
710 RectF::Union (branchCover
, branchCover
, glyphCenter
);
712 if (graphics
.graphics
)
713 graphics
.graphics
->FillRectangle (&brush
, branchCover
);
714 else if (graphics
.pSVG
)
715 graphics
.pSVG
->RoundedRectangle((int)branchCover
.X
, (int)branchCover
.Y
, (int)branchCover
.Width
, (int)branchCover
.Height
,
722 void CRevisionGraphWnd::DrawMarker
723 ( GraphicsDevice
& graphics
724 , const RectF
& noderect
725 , MarkerPosition
/*position*/
726 , int /*relPosition*/
727 , const Color
& penColor
730 REAL width
= 4*this->m_fZoomFactor
<1? 1: 4*this->m_fZoomFactor
;
731 Pen
pen(penColor
,width
);
732 DrawRoundedRect(graphics
, penColor
, (int)width
, &pen
, Color(0,0,0), nullptr, noderect
);
736 REAL x
= max(1, 10 * this->m_fZoomFactor
);
737 REAL y1
= max(1, 25 * this->m_fZoomFactor
);
738 REAL y2
= max(1, 5 * this->m_fZoomFactor
);
739 if(graphics
.graphics
)
741 graphics
.graphics
->DrawLine(&pen
, noderect
.X
+ x
, noderect
.Y
- y1
, noderect
.X
+ x
, noderect
.Y
- y2
);
742 if (m_SelectedEntry2
)
745 base
.AppendFormat(IDS_PROC_DIFF_BASE
);
747 SolidBrush
blackbrush(penColor
);
748 Gdiplus::Font
font(CAppUtils::GetLogFontName(), (REAL
)m_nFontSize
, FontStyleRegular
);
749 graphics
.graphics
->DrawString(base
, base
.GetLength(), &font
, Gdiplus::PointF(noderect
.X
+ x
+ width
, noderect
.Y
- y1
), &blackbrush
);
756 REAL x1
= max(1, 5 * this->m_fZoomFactor
);
757 REAL x2
= max(1, 15 * this->m_fZoomFactor
);
758 REAL y1
= max(1, 25 * this->m_fZoomFactor
);
759 REAL y2
= max(1, 5 * this->m_fZoomFactor
);
760 if(graphics
.graphics
)
762 graphics
.graphics
->DrawLine(&pen
, noderect
.X
+ x1
, noderect
.Y
- y1
, noderect
.X
+ x1
, noderect
.Y
- y2
);
763 graphics
.graphics
->DrawLine(&pen
, noderect
.X
+ x2
, noderect
.Y
- y1
, noderect
.X
+ x2
, noderect
.Y
- y2
);
769 void CRevisionGraphWnd::DrawStripes (GraphicsDevice
& graphics
, const CSize
& offset
)
771 // we need to fill this visible area of the the screen
772 // (even if there is graph in that part)
775 if (graphics
.graphics
)
776 graphics
.graphics
->GetVisibleClipBounds (&clipRect
);
778 // don't show stripes if we don't have multiple roots
780 CSyncPointer
<const ILayoutRectList
> trees (m_state
.GetTrees());
781 if (trees
->GetCount() < 2)
784 // iterate over all trees
786 for ( index_t i
= 0, count
= trees
->GetCount(); i
< count
; ++i
)
788 // screen coordinates covered by the tree
790 CRect tree
= trees
->GetRect(i
);
791 REAL left
= tree
.left
* m_fZoomFactor
;
792 REAL right
= tree
.right
* m_fZoomFactor
;
793 RectF
rect ( left
- offset
.cx
795 , i
+1 == count
? clipRect
.Width
: right
- left
800 if (rect
.IntersectsWith (clipRect
))
802 // draw the background stripe
804 Color
color ( (i
& 1) == 0
805 ? m_Colors
.GetColor (CColors::gdpStripeColor1
)
806 : m_Colors
.GetColor (CColors::gdpStripeColor2
));
807 if (graphics
.graphics
)
809 SolidBrush
brush (color
);
810 graphics
.graphics
->FillRectangle (&brush
, rect
);
812 else if (graphics
.pSVG
)
813 graphics
.pSVG
->RoundedRectangle((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
,
820 PointF
CRevisionGraphWnd::cutPoint(node v
,double lw
,PointF ps
, PointF pt
)
822 double x
= m_GraphAttr
.x(v
);
823 double y
= m_GraphAttr
.y(v
);
824 double xmin
= x
- this->m_GraphAttr
.width(v
)/2 - lw
/2;
825 double xmax
= x
+ this->m_GraphAttr
.width(v
)/2 + lw
/2;
826 double ymin
= y
- this->m_GraphAttr
.height(v
)/2 - lw
/2;
827 double ymax
= y
+ this->m_GraphAttr
.height(v
)/2 + lw
/2;;
829 double dx
= pt
.X
- ps
.X
;
830 double dy
= pt
.Y
- ps
.Y
;
835 double t
= (ymax
-ps
.Y
) / dy
;
838 if(xmin
<= x
&& x
<= xmax
)
839 return PointF((REAL
)x
, (REAL
)ymax
);
842 } else if(pt
.Y
< ymin
) {
843 double t
= (ymin
-ps
.Y
) / dy
;
846 if(xmin
<= x
&& x
<= xmax
)
847 return PointF((REAL
)x
, (REAL
)ymin
);
854 double t
= (xmax
-ps
.X
) / dx
;
857 if(ymin
<= y
&& y
<= ymax
)
858 return PointF((REAL
)xmax
, (REAL
)y
);
861 } else if(pt
.X
< xmin
) {
862 double t
= (xmin
-ps
.X
) / dx
;
865 if(ymin
<= y
&& y
<= ymax
)
866 return PointF((REAL
)xmin
, (REAL
)y
);
873 void CRevisionGraphWnd::DrawConnections (GraphicsDevice
& graphics
, const CRect
& /*logRect*/, const CSize
& offset
)
875 CArray
<PointF
> points
;
878 if(graphics
.graphics
)
879 graphics
.graphics
->SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias
);
881 float penwidth
= 2*m_fZoomFactor
<1? 1:2*m_fZoomFactor
;
882 Gdiplus::Pen
pen(GetColorFromSysColor(COLOR_WINDOWTEXT
), penwidth
);
884 // iterate over all visible lines
886 forall_edges(e
, m_Graph
)
888 // get connection and point position
889 const DPolyline
&dpl
= this->m_GraphAttr
.bends(e
);
895 pt
.X
= (REAL
)m_GraphAttr
.x(e
->source());
896 pt
.Y
= (REAL
)m_GraphAttr
.y(e
->source());
900 ListConstIterator
<DPoint
> it
;
901 for(it
= dpl
.begin(); it
.valid(); ++it
)
903 pt
.X
= (REAL
)(*it
).m_x
;
904 pt
.Y
= (REAL
)(*it
).m_y
;
908 pt
.X
= (REAL
)m_GraphAttr
.x(e
->target());
909 pt
.Y
= (REAL
)m_GraphAttr
.y(e
->target());
913 points
[0] = this->cutPoint(e
->source(), 1, points
[0], points
[1]);
914 points
[points
.GetCount()-1] = this->cutPoint(e
->target(), 1, points
[points
.GetCount()-1], points
[points
.GetCount()-2]);
915 // draw the connection
917 for (int i
= 0; i
< points
.GetCount(); ++i
)
920 points
[i
].X
= points
[i
].X
* this->m_fZoomFactor
- offset
.cx
;
921 points
[i
].Y
= points
[i
].Y
* this->m_fZoomFactor
- offset
.cy
;
925 if (graphics
.graphics
)
926 graphics
.graphics
->DrawLines(&pen
, points
.GetData(), (INT
)points
.GetCount());
927 else if (graphics
.pSVG
)
928 graphics
.pSVG
->Polyline(points
.GetData(), (int)points
.GetCount(), Color(0,0,0), (int)penwidth
);
929 else if (graphics
.pGraphviz
)
931 CString hash1
= L
'g' + m_logEntries
[e
->target()->index()].ToString().Left(g_Git
.GetShortHASHLength());
932 CString hash2
= L
'g' + m_logEntries
[e
->source()->index()].ToString().Left(g_Git
.GetShortHASHLength());
933 graphics
.pGraphviz
->DrawEdge(hash1
, hash2
);
937 double dx
= points
[1].X
- points
[0].X
;
938 double dy
= points
[1].Y
- points
[0].Y
;
940 double len
= sqrt(dx
*dx
+ dy
*dy
);
941 dx
= m_ArrowSize
* m_fZoomFactor
*dx
/len
;
942 dy
= m_ArrowSize
* m_fZoomFactor
*dy
/len
;
944 double p1_x
, p1_y
, p2_x
, p2_y
;
945 p1_x
= dx
* m_ArrowCos
- dy
* m_ArrowSin
;
946 p1_y
= dx
* m_ArrowSin
+ dy
* m_ArrowCos
;
948 p2_x
= dx
* m_ArrowCos
+ dy
* m_ArrowSin
;
949 p2_y
= -dx
* m_ArrowSin
+ dy
* m_ArrowCos
;
951 //graphics.graphics->DrawLine(&pen, points[0].X,points[0].Y, points[0].X +p1_x,points[0].Y+p1_y);
952 //graphics.graphics->DrawLine(&pen, points[0].X,points[0].Y, points[0].X +p2_x,points[0].Y+p2_y);
956 arrows
[0].X
= points
[0].X
;
957 arrows
[0].Y
= points
[0].Y
;
959 arrows
[1].X
= points
[0].X
+ (REAL
)p1_x
;
960 arrows
[1].Y
= points
[0].Y
+ (REAL
)p1_y
;
962 arrows
[2].X
= points
[0].X
+ (REAL
)dx
*3/5;
963 arrows
[2].Y
= points
[0].Y
+ (REAL
)dy
*3/5;
965 arrows
[3].X
= points
[0].X
+ (REAL
)p2_x
;
966 arrows
[3].Y
= points
[0].Y
+ (REAL
)p2_y
;
968 arrows
[4].X
= points
[0].X
;
969 arrows
[4].Y
= points
[0].Y
;
971 path
.AddLines(arrows
, 5);
972 path
.SetFillMode(FillModeAlternate
);
973 if(graphics
.graphics
)
974 graphics
.graphics
->DrawPath(&pen
, &path
);
975 else if(graphics
.pSVG
)
976 graphics
.pSVG
->DrawPath(arrows
, 5, Color(0,0,0), (int)penwidth
, Color(0,0,0));
980 void CRevisionGraphWnd::DrawTexts (GraphicsDevice
& graphics
, const CRect
& /*logRect*/, const CSize
& offset
)
982 //COLORREF standardTextColor = GetSysColor(COLOR_WINDOWTEXT);
983 if (m_nFontSize
<= 0)
986 // iterate over all visible nodes
989 graphics
.pDC
->SetTextAlign (TA_CENTER
| TA_TOP
);
991 CString fontname
= CAppUtils::GetLogFontName();
993 Gdiplus::Font
font(fontname
, (REAL
)m_nFontSize
, FontStyleRegular
);
994 SolidBrush
blackbrush((ARGB
)Color::Black
);
996 DWORD revGraphUseLocalForCur
= CRegDWORD(L
"Software\\TortoiseGit\\TortoiseProc\\Graph\\RevGraphUseLocalForCur");
999 forall_nodes(v
,m_Graph
)
1001 // get node and position
1003 String label
=this->m_GraphAttr
.labelNode(v
);
1005 RectF
noderect (GetNodeRect (v
, offset
));
1007 // draw the revision text
1008 CGitHash hash
= this->m_logEntries
[v
->index()];
1009 double hight
= noderect
.Height
/ (!m_HashMap
[hash
].empty() ? m_HashMap
[hash
].size() : 1);
1011 if (m_HashMap
.find(hash
) == m_HashMap
.end() || m_HashMap
[hash
].empty())
1014 background
.SetFromCOLORREF (GetSysColor(COLOR_WINDOW
));
1015 Gdiplus::Pen
pen(background
,1.0F
);
1016 Color brightColor
= LimitedScaleColor (background
, RGB(255,0,0), 0.9f
);
1017 Gdiplus::SolidBrush
brush(brightColor
);
1019 DrawRoundedRect(graphics
, background
,1, &pen
, brightColor
, &brush
, noderect
);
1021 if(graphics
.graphics
)
1023 graphics
.graphics
->DrawString(hash
.ToString().Left(g_Git
.GetShortHASHLength()),-1,
1025 Gdiplus::PointF(noderect
.X
+ this->GetLeftRightMargin()*this->m_fZoomFactor
,noderect
.Y
+this->GetTopBottomMargin()*m_fZoomFactor
),
1030 graphics
.pSVG
->Text((int)(noderect
.X
+ this->GetLeftRightMargin() * this->m_fZoomFactor
),
1031 (int)(noderect
.Y
+ this->GetTopBottomMargin() * m_fZoomFactor
+ m_nFontSize
),
1032 CUnicodeUtils::GetUTF8(fontname
), m_nFontSize
, false, false, (ARGB
)Color::Black
,
1033 CUnicodeUtils::GetUTF8(hash
.ToString().Left(g_Git
.GetShortHASHLength())));
1035 if (graphics
.pGraphviz
)
1037 CString shortHash
= hash
.ToString().Left(g_Git
.GetShortHASHLength());
1038 graphics
.pGraphviz
->DrawNode(L
'g' + shortHash
, shortHash
, fontname
, m_nFontSize
, background
, brightColor
, (int)noderect
.Height
);
1042 if (graphics
.pGraphviz
)
1044 CString id
= L
'g' + hash
.ToString().Left(g_Git
.GetShortHASHLength());
1045 graphics
.pGraphviz
->BeginDrawTableNode(id
, fontname
, m_nFontSize
, (int)noderect
.Height
);
1048 for (size_t i
= 0; i
< m_HashMap
[hash
].size(); ++i
)
1051 CString str
= m_HashMap
[hash
][i
];
1054 rect
.X
= (REAL
)noderect
.X
;
1055 rect
.Y
= (REAL
)(noderect
.Y
+ hight
*i
);
1056 rect
.Width
= (REAL
)noderect
.Width
;
1057 rect
.Height
= (REAL
)hight
;
1059 COLORREF colRef
= m_Colors
.GetColor(CColors::OtherRef
);
1061 CGit::REF_TYPE refType
;
1062 shortname
= CGit::GetShortName(str
, &refType
);
1065 case CGit::REF_TYPE::LOCAL_BRANCH
:
1066 if (!revGraphUseLocalForCur
&& shortname
== m_CurrentBranch
)
1067 colRef
= m_Colors
.GetColor(CColors::CurrentBranch
);
1069 colRef
= m_Colors
.GetColor(CColors::LocalBranch
);
1071 case CGit::REF_TYPE::REMOTE_BRANCH
:
1072 colRef
= m_Colors
.GetColor(CColors::RemoteBranch
);
1074 case CGit::REF_TYPE::ANNOTATED_TAG
:
1075 case CGit::REF_TYPE::TAG
:
1076 colRef
= m_Colors
.GetColor(CColors::Tag
);
1078 case CGit::REF_TYPE::STASH
:
1079 colRef
= m_Colors
.GetColor(CColors::Stash
);
1081 case CGit::REF_TYPE::BISECT_GOOD
:
1082 colRef
= m_Colors
.GetColor(CColors::BisectGood
);
1084 case CGit::REF_TYPE::BISECT_BAD
:
1085 colRef
= m_Colors
.GetColor(CColors::BisectBad
);
1087 case CGit::REF_TYPE::BISECT_SKIP
:
1088 colRef
= m_Colors
.GetColor(CColors::BisectSkip
);
1090 case CGit::REF_TYPE::NOTES
:
1091 colRef
= m_Colors
.GetColor(CColors::NoteNode
);
1095 Gdiplus::Color
color(GetRValue(colRef
), GetGValue(colRef
), GetBValue(colRef
));
1096 Gdiplus::Pen
pen(color
);
1097 Gdiplus::SolidBrush
brush(color
);
1100 mask
|= (i
==0)? ROUND_UP
:0;
1101 mask
|= (i
== m_HashMap
[hash
].size()-1)? ROUND_DOWN
:0;
1102 this->DrawRoundedRect(graphics
, color
,1,&pen
, color
,&brush
, rect
,mask
);
1104 if (graphics
.graphics
)
1106 //graphics.graphics->FillRectangle(&SolidBrush(Gdiplus::Color(GetRValue(colRef), GetGValue(colRef), GetBValue(colRef))),
1109 graphics
.graphics
->DrawString(shortname
, shortname
.GetLength(),
1111 Gdiplus::PointF((REAL
)(noderect
.X
+ this->GetLeftRightMargin()*m_fZoomFactor
),
1112 (REAL
)(noderect
.Y
+ this->GetTopBottomMargin()*m_fZoomFactor
+ hight
*i
)),
1115 //graphics.graphics->DrawString(shortname.GetBuffer(), shortname.GetLength(), ::new Gdiplus::Font(graphics.pDC->m_hDC), PointF(noderect.X, noderect.Y + hight * i), nullptr, nullptr);
1118 else if (graphics
.pSVG
)
1119 graphics
.pSVG
->Text((int)(noderect
.X
+ this->GetLeftRightMargin() * m_fZoomFactor
),
1120 (int)(noderect
.Y
+ this->GetTopBottomMargin() * m_fZoomFactor
+ hight
* i
+ m_nFontSize
),
1121 CUnicodeUtils::GetUTF8(fontname
), m_nFontSize
,
1122 false, false, (ARGB
)Color::Black
, CUnicodeUtils::GetUTF8(shortname
));
1123 else if (graphics
.pGraphviz
)
1124 graphics
.pGraphviz
->DrawTableNode(shortname
, color
);
1127 if (graphics
.pGraphviz
)
1128 graphics
.pGraphviz
->EndDrawTableNode();
1130 if ((m_SelectedEntry1
== v
))
1131 DrawMarker(graphics
, noderect
, mpLeft
, 0, GetColorFromSysColor(COLOR_HIGHLIGHT
), 1);
1133 if ((m_SelectedEntry2
== v
))
1134 DrawMarker(graphics
, noderect
, mpLeft
, 0, Color(136,0, 21), 2);
1140 void CRevisionGraphWnd::DrawCurrentNodeGlyphs (GraphicsDevice
& graphics
, Image
* glyphs
, const CSize
& offset
)
1142 CSyncPointer
<const ILayoutNodeList
> nodeList (m_state
.GetNodes());
1144 = m_state
.GetOptions()->GetOption
<CUpsideDownLayout
>()->IsActive();
1146 // don't draw glyphs if we are outside the client area
1147 // (e.g. within a scrollbar)
1150 GetCursorPos (&point
);
1151 ScreenToClient (&point
);
1152 if (!GetClientRect().PtInRect (point
))
1155 // expansion glypths etc.
1157 m_hoverIndex
= GetHitNode (point
);
1158 m_hoverGlyphs
= GetHoverGlyphs (point
);
1160 if ((m_hoverIndex
!= NO_INDEX
) || (m_hoverGlyphs
!= 0))
1162 index_t nodeIndex
= m_hoverIndex
== NO_INDEX
1163 ? GetHitNode (point
, CSize (GLYPH_SIZE
, GLYPH_SIZE
/ 2))
1166 if (nodeIndex
>= nodeList
->GetCount())
1169 ILayoutNodeList::SNode node
= nodeList
->GetNode (nodeIndex
);
1170 RectF
noderect (GetNodeRect (node
, offset
));
1172 DWORD flags
= m_state
.GetNodeStates()->GetFlags (node
.node
);
1174 IndicateGlyphDirection (graphics
, nodeList
.get(), node
, noderect
, m_hoverGlyphs
, upsideDown
, offset
);
1175 DrawGlyphs (graphics
, glyphs
, node
.node
, noderect
, flags
, m_hoverGlyphs
, upsideDown
);
1180 void CRevisionGraphWnd::DrawGraph(GraphicsDevice
& graphics
, const CRect
& rect
, int nVScrollPos
, int nHScrollPos
, bool bDirectDraw
)
1182 CMemDC
* memDC
= nullptr;
1187 memDC
= new CMemDC (*graphics
.pDC
, rect
);
1188 graphics
.pDC
= &memDC
->GetDC();
1191 graphics
.pDC
->FillSolidRect(rect
, GetSysColor(COLOR_WINDOW
));
1192 graphics
.pDC
->SetBkMode(TRANSPARENT
);
1195 // preparation & sync
1197 //CSyncPointer<CAllRevisionGraphOptions> options (m_state.GetOptions());
1198 ClearVisibleGlyphs (rect
);
1200 // transform visible
1202 CSize
offset (nHScrollPos
, nVScrollPos
);
1203 CRect
logRect ( (int)(offset
.cx
/ m_fZoomFactor
)-1
1204 , (int)(offset
.cy
/ m_fZoomFactor
)-1
1205 , (int)((rect
.Width() + offset
.cx
) / m_fZoomFactor
) + 1
1206 , (int)((rect
.Height() + offset
.cy
) / m_fZoomFactor
) + 1);
1208 // draw the different components
1212 Graphics
* gcs
= Graphics::FromHDC(*graphics
.pDC
);
1213 graphics
.graphics
= gcs
;
1214 gcs
->SetPageUnit (UnitPixel
);
1215 gcs
->SetInterpolationMode (InterpolationModeHighQualityBicubic
);
1216 gcs
->SetSmoothingMode(SmoothingModeAntiAlias
);
1217 gcs
->SetClip(RectF(Gdiplus::REAL(rect
.left
), Gdiplus::REAL(rect
.top
), Gdiplus::REAL(rect
.Width()), Gdiplus::REAL(rect
.Height())));
1220 // if (options->GetOption<CShowTreeStripes>()->IsActive())
1221 // DrawStripes (graphics, offset);
1223 //if (m_fZoomFactor > SHADOW_ZOOM_THRESHOLD)
1224 // DrawShadows (graphics, logRect, offset);
1226 Bitmap
glyphs (AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_REVGRAPHGLYPHS
));
1228 DrawTexts (graphics
, logRect
, offset
);
1229 DrawConnections (graphics
, logRect
, offset
);
1230 //if (m_showHoverGlyphs)
1231 // DrawCurrentNodeGlyphs (graphics, &glyphs, offset);
1235 if ((!bDirectDraw
)&&(m_Preview
.GetSafeHandle())&&(m_bShowOverview
)&&(graphics
.pDC
))
1237 // draw the overview image rectangle in the top right corner
1238 CMyMemDC
memDC2(graphics
.pDC
, true);
1239 memDC2
.SetWindowOrg(0, 0);
1240 HBITMAP oldhbm
= (HBITMAP
)memDC2
.SelectObject(&m_Preview
);
1241 graphics
.pDC
->BitBlt(rect
.Width()-m_previewWidth
, rect
.Height() - m_previewHeight
, m_previewWidth
, m_previewHeight
,
1242 &memDC2
, 0, 0, SRCCOPY
);
1243 memDC2
.SelectObject(oldhbm
);
1244 // draw the border for the overview rectangle
1245 m_OverviewRect
.left
= rect
.Width()-m_previewWidth
;
1246 m_OverviewRect
.top
= rect
.Height()- m_previewHeight
;
1247 m_OverviewRect
.right
= rect
.Width();
1248 m_OverviewRect
.bottom
= rect
.Height();
1249 graphics
.pDC
->DrawEdge(&m_OverviewRect
, EDGE_BUMP
, BF_RECT
);
1250 // now draw a rectangle where the current view is located in the overview
1252 LONG width
= (long)(rect
.Width() * m_previewZoom
/ m_fZoomFactor
);
1253 LONG height
= (long)(rect
.Height() * m_previewZoom
/ m_fZoomFactor
);
1254 LONG xpos
= (long)(nHScrollPos
* m_previewZoom
/ m_fZoomFactor
);
1255 LONG ypos
= (long)(nVScrollPos
* m_previewZoom
/ m_fZoomFactor
);
1257 tempRect
.left
= rect
.Width()-m_previewWidth
+xpos
;
1258 tempRect
.top
= rect
.Height() - m_previewHeight
+ ypos
;
1259 tempRect
.right
= tempRect
.left
+ width
;
1260 tempRect
.bottom
= tempRect
.top
+ height
;
1261 // make sure the position rect is not bigger than the preview window itself
1262 ::IntersectRect(&m_OverviewPosRect
, &m_OverviewRect
, &tempRect
);
1264 RectF
rect2 ( (float)m_OverviewPosRect
.left
, (float)m_OverviewPosRect
.top
1265 , (float)m_OverviewPosRect
.Width(), (float)m_OverviewPosRect
.Height());
1266 if (graphics
.graphics
)
1268 SolidBrush
brush (Color (64, 0, 0, 0));
1269 graphics
.graphics
->FillRectangle (&brush
, rect2
);
1270 graphics
.pDC
->DrawEdge(&m_OverviewPosRect
, EDGE_BUMP
, BF_RECT
);
1274 // flush changes to screen
1276 delete graphics
.graphics
;
1280 void CRevisionGraphWnd::SetNodeRect(GraphicsDevice
& graphics
, ogdf::node
*pnode
, CGitHash rev
, int mode
)
1282 //multi - line mode. One RefName is one new line
1283 CString fontname
= CAppUtils::GetLogFontName();
1286 if(this->m_HashMap
.find(rev
) == m_HashMap
.end())
1288 CString shorthash
= rev
.ToString().Left(g_Git
.GetShortHASHLength());
1290 if(graphics
.graphics
)
1292 //GetTextExtentPoint32(graphics.pDC->m_hDC, shorthash.GetBuffer(), shorthash.GetLength(), &size);
1293 Gdiplus::Font
font(fontname
, (REAL
)m_nFontSize
, FontStyleRegular
);
1294 graphics
.graphics
->MeasureString(shorthash
, shorthash
.GetLength(),
1296 Gdiplus::PointF(0,0), &rect
);
1298 m_GraphAttr
.width(*pnode
) = this->GetLeftRightMargin()*2 + rect
.Width
;
1299 m_GraphAttr
.height(*pnode
) = this->GetTopBottomMargin()*2 + rect
.Height
;
1306 for (size_t i
= 0; i
< m_HashMap
[rev
].size(); ++i
)
1309 CString shortref
= m_HashMap
[rev
][i
];
1310 shortref
= CGit::GetShortName(shortref
, nullptr);
1313 Gdiplus::Font
font(fontname
, (REAL
)m_nFontSize
, FontStyleRegular
);
1314 graphics
.graphics
->MeasureString(shortref
, shortref
.GetLength(),
1316 Gdiplus::PointF(0,0), &rect
);
1317 if(rect
.Width
> xmax
)
1319 if(rect
.Height
> ymax
)
1324 m_GraphAttr
.width(*pnode
) = this->GetLeftRightMargin()*2 + xmax
;
1325 m_GraphAttr
.height(*pnode
) = (this->GetTopBottomMargin()*2 + ymax
) * lines
;