1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2011, 2015 - TortoiseSVN
4 // Copyright (C) 2012-2013, 2015-2016 - 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"
40 static char THIS_FILE
[] = __FILE__
;
43 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
;
70 m_lfBaseFont
.lfHeight
= -MulDiv(m_nFontSize
, GetDeviceCaps(pDC
->m_hDC
, LOGPIXELSY
), 72);
72 // use the empty font name, so GDI takes the first font which matches
73 // the specs. Maybe this will help render chinese/japanese chars correctly.
74 wcsncpy_s(m_lfBaseFont
.lfFaceName
, L
"MS Shell Dlg 2", _countof(m_lfBaseFont
.lfFaceName
) - 1);
75 if (!m_apFonts
[nIndex
]->CreateFontIndirect(&m_lfBaseFont
))
77 delete m_apFonts
[nIndex
];
78 m_apFonts
[nIndex
] = nullptr;
79 return CWnd::GetFont();
82 return m_apFonts
[nIndex
];
85 BOOL
CRevisionGraphWnd::OnEraseBkgnd(CDC
* /*pDC*/)
90 void CRevisionGraphWnd::OnPaint()
92 CPaintDC
dc(this); // device context for painting
93 CRect rect
= GetClientRect();
95 if (IsUpdateJobRunning())
97 CString fetch
= CString(MAKEINTRESOURCE(IDS_PROC_LOADING
));
98 dc
.FillSolidRect(rect
, ::GetSysColor(COLOR_APPWORKSPACE
));
99 dc
.ExtTextOut(20, 20, ETO_CLIPPED
, nullptr, fetch
, nullptr);
103 }else if (this->m_Graph
.empty())
105 CString sNoGraphText
;
106 sNoGraphText
.LoadString(IDS_REVGRAPH_ERR_NOGRAPH
);
107 dc
.FillSolidRect(rect
, RGB(255,255,255));
108 dc
.ExtTextOut(20, 20, ETO_CLIPPED
, nullptr, sNoGraphText
, nullptr);
114 DrawGraph(dev
, rect
, GetScrollPos(SB_VERT
), GetScrollPos(SB_HORZ
), false);
117 void CRevisionGraphWnd::ClearVisibleGlyphs (const CRect
& /*rect*/)
120 float glyphSize
= GLYPH_SIZE
* m_fZoomFactor
;
122 CSyncPointer
<CRevisionGraphState::TVisibleGlyphs
>
123 visibleGlyphs (m_state
.GetVisibleGlyphs());
125 for (size_t i
= visibleGlyphs
->size(), count
= i
; i
> 0; --i
)
127 const PointF
& leftTop
= (*visibleGlyphs
)[i
-1].leftTop
;
128 CRect
glyphRect ( static_cast<int>(leftTop
.X
)
129 , static_cast<int>(leftTop
.Y
)
130 , static_cast<int>(leftTop
.X
+ glyphSize
)
131 , static_cast<int>(leftTop
.Y
+ glyphSize
));
133 if (CRect().IntersectRect (glyphRect
, rect
))
135 (*visibleGlyphs
)[i
-1] = (*visibleGlyphs
)[--count
];
136 visibleGlyphs
->pop_back();
142 void CRevisionGraphWnd::CutawayPoints (const RectF
& rect
, float cutLen
, TCutRectangle
& result
)
144 result
[0] = PointF (rect
.X
, rect
.Y
+ cutLen
);
145 result
[1] = PointF (rect
.X
+ cutLen
, rect
.Y
);
146 result
[2] = PointF (rect
.GetRight() - cutLen
, rect
.Y
);
147 result
[3] = PointF (rect
.GetRight(), rect
.Y
+ cutLen
);
148 result
[4] = PointF (rect
.GetRight(), rect
.GetBottom() - cutLen
);
149 result
[5] = PointF (rect
.GetRight() - cutLen
, rect
.GetBottom());
150 result
[6] = PointF (rect
.X
+ cutLen
, rect
.GetBottom());
151 result
[7] = PointF (rect
.X
, rect
.GetBottom() - cutLen
);
154 void CRevisionGraphWnd::DrawRoundedRect (GraphicsDevice
& graphics
, const Color
& penColor
, int penWidth
, const Pen
* pen
, const Color
& fillColor
, const Brush
* brush
, const RectF
& rect
, int mask
)
156 enum {POINT_COUNT
= 8};
158 float radius
= CORNER_SIZE
* m_fZoomFactor
;
159 PointF points
[POINT_COUNT
];
160 CutawayPoints (rect
, radius
, points
);
162 if (graphics
.graphics
)
168 path
.AddArc (points
[0].X
, points
[1].Y
, radius
, radius
, 180, 90);
169 path
.AddArc (points
[2].X
, points
[2].Y
, radius
, radius
, 270, 90);
171 path
.AddLine(points
[0].X
, points
[1].Y
, points
[3].X
, points
[2].Y
);
173 if(mask
& ROUND_DOWN
)
175 path
.AddArc (points
[5].X
, points
[4].Y
, radius
, radius
, 0, 90);
176 path
.AddArc (points
[7].X
, points
[7].Y
, radius
, radius
, 90, 90);
179 path
.AddLine(points
[3].X
, points
[3].Y
, points
[4].X
, points
[5].Y
);
180 path
.AddLine(points
[4].X
, points
[5].Y
, points
[7].X
, points
[6].Y
);
183 points
[0].Y
-= radius
/ 2;
184 path
.AddLine (points
[7], points
[0]);
187 graphics
.graphics
->FillPath (brush
, &path
);
189 graphics
.graphics
->DrawPath (pen
, &path
);
191 else if (graphics
.pSVG
)
192 graphics
.pSVG
->RoundedRectangle((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
, penColor
, penWidth
, fillColor
, (int)radius
, mask
);
195 void CRevisionGraphWnd::DrawOctangle (GraphicsDevice
& graphics
, const Color
& penColor
, int penWidth
, const Pen
* pen
, const Color
& fillColor
, const Brush
* brush
, const RectF
& rect
)
197 enum {POINT_COUNT
= 8};
199 // show left & right edges of low boxes as "<===>"
201 float minCutAway
= min (CORNER_SIZE
* m_fZoomFactor
, rect
.Height
/ 2);
203 // larger boxes: remove 25% of the shorter side
205 float suggestedCutAway
= min (rect
.Height
, rect
.Width
) / 4;
207 // use the more visible one of the former two
209 PointF points
[POINT_COUNT
];
210 CutawayPoints (rect
, max (minCutAway
, suggestedCutAway
), points
);
214 if (graphics
.graphics
)
217 graphics
.graphics
->FillPolygon (brush
, points
, POINT_COUNT
);
219 graphics
.graphics
->DrawPolygon (pen
, points
, POINT_COUNT
);
221 else if (graphics
.pSVG
)
223 graphics
.pSVG
->Polygon(points
, POINT_COUNT
, penColor
, penWidth
, fillColor
);
229 void CRevisionGraphWnd::DrawShape (GraphicsDevice
& graphics
, const Color
& penColor
, int penWidth
, const Pen
* pen
, const Color
& fillColor
, const Brush
* brush
, const RectF
& rect
, NodeShape shape
)
234 if (graphics
.graphics
)
237 graphics
.graphics
->FillRectangle (brush
, rect
);
239 graphics
.graphics
->DrawRectangle (pen
, rect
);
241 else if (graphics
.pSVG
)
243 graphics
.pSVG
->RoundedRectangle((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
, penColor
, penWidth
, fillColor
);
247 DrawRoundedRect (graphics
, penColor
, penWidth
, pen
, fillColor
, brush
, rect
);
250 DrawOctangle (graphics
, penColor
, penWidth
, pen
, fillColor
, brush
, rect
);
253 if (graphics
.graphics
)
256 graphics
.graphics
->FillEllipse (brush
, rect
);
258 graphics
.graphics
->DrawEllipse(pen
, rect
);
260 else if (graphics
.pSVG
)
261 graphics
.pSVG
->Ellipse((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
, penColor
, penWidth
, fillColor
);
264 ASSERT(FALSE
); //unknown type
270 inline BYTE
LimitedScaleColor (BYTE c1
, BYTE c2
, float factor
)
272 BYTE scaled
= c2
+ (BYTE
)((c1
-c2
)*factor
);
278 Color
LimitedScaleColor (const Color
& c1
, const Color
& c2
, float factor
)
280 return Color ( LimitedScaleColor (c1
.GetA(), c2
.GetA(), factor
)
281 , LimitedScaleColor (c1
.GetR(), c2
.GetR(), factor
)
282 , LimitedScaleColor (c1
.GetG(), c2
.GetG(), factor
)
283 , LimitedScaleColor (c1
.GetB(), c2
.GetB(), factor
));
286 inline BYTE
Darken (BYTE c
)
290 : BYTE(int(2*c
) - 0x100);
293 Color
Darken (const Color
& c
)
298 , Darken (c
.GetB()));
301 BYTE
MaxComponentDiff (const Color
& c1
, const Color
& c2
)
303 int rDiff
= abs ((int)c1
.GetR() - (int)c2
.GetR());
304 int gDiff
= abs ((int)c1
.GetG() - (int)c2
.GetG());
305 int bDiff
= abs ((int)c1
.GetB() - (int)c2
.GetB());
307 return (BYTE
) max (max (rDiff
, gDiff
), bDiff
);
311 void CRevisionGraphWnd::DrawShadow (GraphicsDevice
& graphics
, const RectF
& rect
,
312 Color shadowColor
, NodeShape shape
)
317 shadow
.Offset (2, 2);
319 Pen
pen (shadowColor
);
320 SolidBrush
brush (shadowColor
);
322 DrawShape (graphics
, shadowColor
, 1, &pen
, shadowColor
, &brush
, shadow
, shape
);
326 RectF
CRevisionGraphWnd::TransformRectToScreen (const CRect
& rect
, const CSize
& offset
) const
328 PointF
leftTop ( rect
.left
* m_fZoomFactor
329 , rect
.top
* m_fZoomFactor
);
330 return RectF ( leftTop
.X
- offset
.cx
331 , leftTop
.Y
- offset
.cy
332 , rect
.right
* m_fZoomFactor
- leftTop
.X
- 1
333 , rect
.bottom
* m_fZoomFactor
- leftTop
.Y
);
337 RectF
CRevisionGraphWnd::GetNodeRect (const node
& node
, const CSize
& offset
) const
339 // get node and position
342 rect
.left
= (int) (this->m_GraphAttr
.x(node
) - m_GraphAttr
.width(node
)/2);
343 rect
.top
= (int) (this->m_GraphAttr
.y(node
) - m_GraphAttr
.height(node
)/2);
344 rect
.bottom
= (int)( rect
.top
+ m_GraphAttr
.height(node
));
345 rect
.right
= (int)(rect
.left
+ m_GraphAttr
.width(node
));
347 RectF
noderect (TransformRectToScreen (rect
, offset
));
349 // show two separate lines for touching nodes,
350 // unless the scale is too small
352 if (noderect
.Height
> 15.0f
)
353 noderect
.Height
-= 1.0f
;
363 isionGraphWnd::GetBranchCover
364 ( const ILayoutNodeList
* nodeList
367 , const CSize
& offset
)
369 // construct a rect that covers the respective branch
371 CRect
cover (0, 0, 0, 0);
372 while (nodeIndex
!= NO_INDEX
)
374 ILayoutNodeList::SNode node
= nodeList
->GetNode (nodeIndex
);
377 const CVisibleGraphNode
* nextNode
= upward
378 ? node
.node
->GetPrevious()
379 : node
.node
->GetNext();
381 nodeIndex
= !nextNode
? NO_INDEX
: nextNode
->GetIndex();
384 // expand it just a little to make it look nicer
386 cover
.InflateRect (10, 2, 10, 2);
388 // and now, transfrom it
390 return TransformRectToScreen (cover
, offset
);
395 void CRevisionGraphWnd::DrawShadows (GraphicsDevice
& graphics
, const CRect
& logRect
, const CSize
& offset
)
397 // shadow color to use
400 background
.SetFromCOLORREF (GetSysColor(COLOR_WINDOW
));
402 textColor
.SetFromCOLORREF (GetSysColor(COLOR_WINDOWTEXT
));
404 Color shadowColor
= LimitedScaleColor (background
, ARGB (Color::Black
), 0.5f
);
406 // iterate over all visible nodes
408 CSyncPointer
<const ILayoutNodeList
> nodes (m_state
.GetNodes());
409 for ( index_t index
= nodes
->GetFirstVisible (logRect
)
411 ; index
= nodes
->GetNextVisible (index
, logRect
))
413 // get node and position
415 ILayoutNodeList::SNode node
= nodes
->GetNode (index
);
416 RectF
noderect (GetNodeRect (node
, offset
));
422 case ILayoutNodeList::SNode::STYLE_DELETED
:
423 case ILayoutNodeList::SNode::STYLE_RENAMED
:
424 DrawShadow (graphics
, noderect
, shadowColor
, TSVNOctangle
);
426 case ILayoutNodeList::SNode::STYLE_ADDED
:
427 DrawShadow(graphics
, noderect
, shadowColor
, TSVNRoundRect
);
429 case ILayoutNodeList::SNode::STYLE_LAST
:
430 DrawShadow(graphics
, noderect
, shadowColor
, TSVNEllipse
);
433 DrawShadow(graphics
, noderect
, shadowColor
, TSVNRectangle
);
441 void CRevisionGraphWnd::DrawSquare
442 ( GraphicsDevice
& graphics
443 , const PointF
& leftTop
444 , const Color
& lightColor
445 , const Color
& darkColor
446 , const Color
& penColor
)
448 float squareSize
= MARKER_SIZE
* m_fZoomFactor
;
450 PointF
leftBottom (leftTop
.X
, leftTop
.Y
+ squareSize
);
451 RectF
square (leftTop
, SizeF (squareSize
, squareSize
));
453 if (graphics
.graphics
)
455 LinearGradientBrush
lgBrush (leftTop
, leftBottom
, lightColor
, darkColor
);
456 graphics
.graphics
->FillRectangle (&lgBrush
, square
);
457 if (squareSize
> 4.0f
)
460 graphics
.graphics
->DrawRectangle (&pen
, square
);
463 else if (graphics
.pSVG
)
465 graphics
.pSVG
->GradientRectangle((int)square
.X
, (int)square
.Y
, (int)square
.Width
, (int)square
.Height
,
466 lightColor
, darkColor
, penColor
);
471 void CRevisionGraphWnd::DrawGlyph
472 ( GraphicsDevice
& graphics
474 , const PointF
& leftTop
476 , GlyphPosition position
)
480 if (glyph
== NoGlyph
)
483 // bitmap source area
485 REAL x
= ((REAL
)position
+ (REAL
)glyph
) * GLYPH_BITMAP_SIZE
;
487 // screen target area
489 float glyphSize
= GLYPH_SIZE
* m_fZoomFactor
;
490 RectF
target (leftTop
, SizeF (glyphSize
, glyphSize
));
494 if (graphics
.graphics
)
496 graphics
.graphics
->DrawImage ( glyphs
498 , x
, 0.0f
, GLYPH_BITMAP_SIZE
, GLYPH_BITMAP_SIZE
499 , UnitPixel
, nullptr, nullptr, nullptr);
501 else if (graphics
.pSVG
)
503 // instead of inserting a bitmap, draw a
504 // round rectangle instead.
505 // Embedding images would blow up the resulting
506 // svg file a lot, and the round rectangle
509 // images could be embedded like this:
510 // <image y="100" x="100" id="imgId1234" xlink:href="data:image/png;base64,...base64endodeddata..." height="16" width="16" />
512 graphics
.pSVG
->RoundedRectangle((int)target
.X
, (int)target
.Y
, (int)target
.Width
, (int)target
.Height
,
513 Color(0,0,0), 2, Color(255,255,255), (int)(target
.Width
/3.0));
519 void CRevisionGraphWnd::DrawGlyphs
520 ( GraphicsDevice
& graphics
522 , const CVisibleGraphNode
* node
523 , const PointF
& center
526 , GlyphPosition position
531 // don't show collapse and cut glyths by default
533 if (!showAll
&& ((glyph1
== CollapseGlyph
) || (glyph1
== SplitGlyph
)))
535 if (!showAll
&& ((glyph2
== CollapseGlyph
) || (glyph2
== SplitGlyph
)))
538 // glyth2 shall be set only if 2 glyphs are in use
540 if (glyph1
== NoGlyph
)
542 std::swap (glyph1
, glyph2
);
543 std::swap (state1
, state2
);
548 if (glyph1
== NoGlyph
)
553 CSyncPointer
<CRevisionGraphState::TVisibleGlyphs
>
554 visibleGlyphs (m_state
.GetVisibleGlyphs());
556 float squareSize
= GLYPH_SIZE
* m_fZoomFactor
;
557 if (glyph2
== NoGlyph
)
559 PointF
leftTop (center
.X
- 0.5f
* squareSize
, center
.Y
- 0.5f
* squareSize
);
560 DrawGlyph (graphics
, glyphs
, leftTop
, glyph1
, position
);
561 visibleGlyphs
->push_back
562 (CRevisionGraphState::SVisibleGlyph (state1
, leftTop
, node
));
566 PointF
leftTop1 (center
.X
- squareSize
, center
.Y
- 0.5f
* squareSize
);
567 DrawGlyph (graphics
, glyphs
, leftTop1
, glyph1
, position
);
568 visibleGlyphs
->push_back
569 (CRevisionGraphState::SVisibleGlyph (state1
, leftTop1
, node
));
571 PointF
leftTop2 (center
.X
, center
.Y
- 0.5f
* squareSize
);
572 DrawGlyph (graphics
, glyphs
, leftTop2
, glyph2
, position
);
573 visibleGlyphs
->push_back
574 (CRevisionGraphState::SVisibleGlyph (state2
, leftTop2
, node
));
580 void CRevisionGraphWnd::DrawGlyphs
581 ( GraphicsDevice
& graphics
583 , const CVisibleGraphNode
* node
584 , const RectF
& nodeRect
591 if ((state
== 0) && (allowed
== 0))
596 PointF
topCenter (0.5f
* nodeRect
.GetLeft() + 0.5f
* nodeRect
.GetRight(), nodeRect
.GetTop());
597 PointF
rightCenter (nodeRect
.GetRight(), 0.5f
* nodeRect
.GetTop() + 0.5f
* nodeRect
.GetBottom());
598 PointF
bottomCenter (0.5f
* nodeRect
.GetLeft() + 0.5f
* nodeRect
.GetRight(), nodeRect
.GetBottom());
600 DrawGlyphs ( graphics
603 , upsideDown
? bottomCenter
: topCenter
604 , (state
& CGraphNodeStates::COLLAPSED_ABOVE
) ? ExpandGlyph
: CollapseGlyph
605 , (state
& CGraphNodeStates::SPLIT_ABOVE
) ? JoinGlyph
: SplitGlyph
606 , upsideDown
? Below
: Above
607 , CGraphNodeStates::COLLAPSED_ABOVE
608 , CGraphNodeStates::SPLIT_ABOVE
609 , (allowed
& CGraphNodeStates::COLLAPSED_ABOVE
) != 0);
611 DrawGlyphs ( graphics
615 , (state
& CGraphNodeStates::COLLAPSED_RIGHT
) ? ExpandGlyph
: CollapseGlyph
616 , (state
& CGraphNodeStates::SPLIT_RIGHT
) ? JoinGlyph
: SplitGlyph
618 , CGraphNodeStates::COLLAPSED_RIGHT
619 , CGraphNodeStates::SPLIT_RIGHT
620 , (allowed
& CGraphNodeStates::COLLAPSED_RIGHT
) != 0);
622 DrawGlyphs ( graphics
625 , upsideDown
? topCenter
: bottomCenter
626 , (state
& CGraphNodeStates::COLLAPSED_BELOW
) ? ExpandGlyph
: CollapseGlyph
627 , (state
& CGraphNodeStates::SPLIT_BELOW
) ? JoinGlyph
: SplitGlyph
628 , upsideDown
? Above
: Below
629 , CGraphNodeStates::COLLAPSED_BELOW
630 , CGraphNodeStates::SPLIT_BELOW
631 , (allowed
& CGraphNodeStates::COLLAPSED_BELOW
) != 0);
636 void CRevisionGraphWnd::IndicateGlyphDirection
637 ( GraphicsDevice
& graphics
638 , const ILayoutNodeList
* nodeList
639 , const ILayoutNodeList::SNode
& node
640 , const RectF
& nodeRect
643 , const CSize
& offset
)
650 // where to place the indication?
652 bool indicateAbove
= (glyphs
& CGraphNodeStates::COLLAPSED_ABOVE
) != 0;
653 bool indicateRight
= (glyphs
& CGraphNodeStates::COLLAPSED_RIGHT
) != 0;
654 bool indicateBelow
= (glyphs
& CGraphNodeStates::COLLAPSED_BELOW
) != 0;
656 // fill indication area a semi-transparent blend of red
657 // and the background color
660 color
.SetFromCOLORREF (GetSysColor(COLOR_WINDOW
));
661 color
.SetValue ((color
.GetValue() & 0x807f7f7f) + 0x800000);
663 SolidBrush
brush (color
);
665 // draw the indication (only one condition should match)
667 RectF glyphCenter
= (indicateAbove
^ upsideDown
)
668 ? RectF (nodeRect
.X
, nodeRect
.Y
- 1.0f
, 0.0f
, 0.0f
)
669 : RectF (nodeRect
.X
, nodeRect
.GetBottom() - 1.0f
, 0.0f
, 0.0f
);
673 const CVisibleGraphNode
* firstAffected
= node
.node
->GetSource();
675 assert (firstAffected
);
677 = GetBranchCover (nodeList
, firstAffected
->GetIndex(), true, offset
);
678 RectF::Union (branchCover
, branchCover
, glyphCenter
);
680 if (graphics
.graphics
)
681 graphics
.graphics
->FillRectangle (&brush
, branchCover
);
682 else if (graphics
.pSVG
)
683 graphics
.pSVG
->RoundedRectangle((int)branchCover
.X
, (int)branchCover
.Y
, (int)branchCover
.Width
, (int)branchCover
.Height
,
689 for ( const CVisibleGraphNode::CCopyTarget
* branch
690 = node
.node
->GetFirstCopyTarget()
692 ; branch
= branch
->next())
695 = GetBranchCover (nodeList
, branch
->value()->GetIndex(), false, offset
);
696 if (graphics
.graphics
)
697 graphics
.graphics
->FillRectangle (&brush
, branchCover
);
698 else if (graphics
.pSVG
)
699 graphics
.pSVG
->RoundedRectangle((int)branchCover
.X
, (int)branchCover
.Y
, (int)branchCover
.Width
, (int)branchCover
.Height
,
706 const CVisibleGraphNode
* firstAffected
707 = node
.node
->GetNext();
710 = GetBranchCover (nodeList
, firstAffected
->GetIndex(), false, offset
);
711 RectF::Union (branchCover
, branchCover
, glyphCenter
);
713 if (graphics
.graphics
)
714 graphics
.graphics
->FillRectangle (&brush
, branchCover
);
715 else if (graphics
.pSVG
)
716 graphics
.pSVG
->RoundedRectangle((int)branchCover
.X
, (int)branchCover
.Y
, (int)branchCover
.Width
, (int)branchCover
.Height
,
723 void CRevisionGraphWnd::DrawMarker
724 ( GraphicsDevice
& graphics
725 , const RectF
& noderect
726 , MarkerPosition
/*position*/
727 , int /*relPosition*/
728 , const Color
& penColor
731 REAL width
= 4*this->m_fZoomFactor
<1? 1: 4*this->m_fZoomFactor
;
732 Pen
pen(penColor
,width
);
733 DrawRoundedRect(graphics
, penColor
, (int)width
, &pen
, Color(0,0,0), nullptr, noderect
);
737 REAL x
= max(1, 10 * this->m_fZoomFactor
);
738 REAL y1
= max(1, 25 * this->m_fZoomFactor
);
739 REAL y2
= max(1, 5 * this->m_fZoomFactor
);
740 if(graphics
.graphics
)
742 graphics
.graphics
->DrawLine(&pen
, noderect
.X
+ x
, noderect
.Y
- y1
, noderect
.X
+ x
, noderect
.Y
- y2
);
743 if (m_SelectedEntry2
)
746 base
.AppendFormat(IDS_PROC_DIFF_BASE
);
748 SolidBrush
blackbrush(penColor
);
749 Gdiplus::Font
font(CAppUtils::GetLogFontName(), (REAL
)m_nFontSize
, FontStyleRegular
);
750 graphics
.graphics
->DrawString(base
, base
.GetLength(), &font
, Gdiplus::PointF(noderect
.X
+ x
+ width
, noderect
.Y
- y1
), &blackbrush
);
757 REAL x1
= max(1, 5 * this->m_fZoomFactor
);
758 REAL x2
= max(1, 15 * this->m_fZoomFactor
);
759 REAL y1
= max(1, 25 * this->m_fZoomFactor
);
760 REAL y2
= max(1, 5 * this->m_fZoomFactor
);
761 if(graphics
.graphics
)
763 graphics
.graphics
->DrawLine(&pen
, noderect
.X
+ x1
, noderect
.Y
- y1
, noderect
.X
+ x1
, noderect
.Y
- y2
);
764 graphics
.graphics
->DrawLine(&pen
, noderect
.X
+ x2
, noderect
.Y
- y1
, noderect
.X
+ x2
, noderect
.Y
- y2
);
770 void CRevisionGraphWnd::DrawStripes (GraphicsDevice
& graphics
, const CSize
& offset
)
772 // we need to fill this visible area of the the screen
773 // (even if there is graph in that part)
776 if (graphics
.graphics
)
777 graphics
.graphics
->GetVisibleClipBounds (&clipRect
);
779 // don't show stripes if we don't have multiple roots
781 CSyncPointer
<const ILayoutRectList
> trees (m_state
.GetTrees());
782 if (trees
->GetCount() < 2)
785 // iterate over all trees
787 for ( index_t i
= 0, count
= trees
->GetCount(); i
< count
; ++i
)
789 // screen coordinates covered by the tree
791 CRect tree
= trees
->GetRect(i
);
792 REAL left
= tree
.left
* m_fZoomFactor
;
793 REAL right
= tree
.right
* m_fZoomFactor
;
794 RectF
rect ( left
- offset
.cx
796 , i
+1 == count
? clipRect
.Width
: right
- left
801 if (rect
.IntersectsWith (clipRect
))
803 // draw the background stripe
805 Color
color ( (i
& 1) == 0
806 ? m_Colors
.GetColor (CColors::gdpStripeColor1
)
807 : m_Colors
.GetColor (CColors::gdpStripeColor2
));
808 if (graphics
.graphics
)
810 SolidBrush
brush (color
);
811 graphics
.graphics
->FillRectangle (&brush
, rect
);
813 else if (graphics
.pSVG
)
814 graphics
.pSVG
->RoundedRectangle((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
,
821 PointF
CRevisionGraphWnd::cutPoint(node v
,double lw
,PointF ps
, PointF pt
)
823 double x
= m_GraphAttr
.x(v
);
824 double y
= m_GraphAttr
.y(v
);
825 double xmin
= x
- this->m_GraphAttr
.width(v
)/2 - lw
/2;
826 double xmax
= x
+ this->m_GraphAttr
.width(v
)/2 + lw
/2;
827 double ymin
= y
- this->m_GraphAttr
.height(v
)/2 - lw
/2;
828 double ymax
= y
+ this->m_GraphAttr
.height(v
)/2 + lw
/2;;
830 double dx
= pt
.X
- ps
.X
;
831 double dy
= pt
.Y
- ps
.Y
;
836 double t
= (ymax
-ps
.Y
) / dy
;
839 if(xmin
<= x
&& x
<= xmax
)
840 return PointF((REAL
)x
, (REAL
)ymax
);
843 } else if(pt
.Y
< ymin
) {
844 double t
= (ymin
-ps
.Y
) / dy
;
847 if(xmin
<= x
&& x
<= xmax
)
848 return PointF((REAL
)x
, (REAL
)ymin
);
855 double t
= (xmax
-ps
.X
) / dx
;
858 if(ymin
<= y
&& y
<= ymax
)
859 return PointF((REAL
)xmax
, (REAL
)y
);
862 } else if(pt
.X
< xmin
) {
863 double t
= (xmin
-ps
.X
) / dx
;
866 if(ymin
<= y
&& y
<= ymax
)
867 return PointF((REAL
)xmin
, (REAL
)y
);
874 void CRevisionGraphWnd::DrawConnections (GraphicsDevice
& graphics
, const CRect
& /*logRect*/, const CSize
& offset
)
876 CArray
<PointF
> points
;
879 if(graphics
.graphics
)
880 graphics
.graphics
->SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias
);
882 float penwidth
= 2*m_fZoomFactor
<1? 1:2*m_fZoomFactor
;
883 Gdiplus::Pen
pen(GetColorFromSysColor(COLOR_WINDOWTEXT
), penwidth
);
885 // iterate over all visible lines
887 forall_edges(e
, m_Graph
)
889 // get connection and point position
890 const DPolyline
&dpl
= this->m_GraphAttr
.bends(e
);
896 pt
.X
= (REAL
)m_GraphAttr
.x(e
->source());
897 pt
.Y
= (REAL
)m_GraphAttr
.y(e
->source());
901 ListConstIterator
<DPoint
> it
;
902 for(it
= dpl
.begin(); it
.valid(); ++it
)
904 pt
.X
= (REAL
)(*it
).m_x
;
905 pt
.Y
= (REAL
)(*it
).m_y
;
909 pt
.X
= (REAL
)m_GraphAttr
.x(e
->target());
910 pt
.Y
= (REAL
)m_GraphAttr
.y(e
->target());
914 points
[0] = this->cutPoint(e
->source(), 1, points
[0], points
[1]);
915 points
[points
.GetCount()-1] = this->cutPoint(e
->target(), 1, points
[points
.GetCount()-1], points
[points
.GetCount()-2]);
916 // draw the connection
918 for (int i
= 0; i
< points
.GetCount(); ++i
)
921 points
[i
].X
= points
[i
].X
* this->m_fZoomFactor
- offset
.cx
;
922 points
[i
].Y
= points
[i
].Y
* this->m_fZoomFactor
- offset
.cy
;
926 if (graphics
.graphics
)
927 graphics
.graphics
->DrawLines(&pen
, points
.GetData(), (INT
)points
.GetCount());
928 else if (graphics
.pSVG
)
929 graphics
.pSVG
->Polyline(points
.GetData(), (int)points
.GetCount(), Color(0,0,0), (int)penwidth
);
930 else if (graphics
.pGraphviz
)
932 CString hash1
= L
'g' + m_logEntries
[e
->target()->index()].ToString().Left(g_Git
.GetShortHASHLength());
933 CString hash2
= L
'g' + m_logEntries
[e
->source()->index()].ToString().Left(g_Git
.GetShortHASHLength());
934 graphics
.pGraphviz
->DrawEdge(hash1
, hash2
);
938 double dx
= points
[1].X
- points
[0].X
;
939 double dy
= points
[1].Y
- points
[0].Y
;
941 double len
= sqrt(dx
*dx
+ dy
*dy
);
942 dx
= m_ArrowSize
* m_fZoomFactor
*dx
/len
;
943 dy
= m_ArrowSize
* m_fZoomFactor
*dy
/len
;
945 double p1_x
, p1_y
, p2_x
, p2_y
;
946 p1_x
= dx
* m_ArrowCos
- dy
* m_ArrowSin
;
947 p1_y
= dx
* m_ArrowSin
+ dy
* m_ArrowCos
;
949 p2_x
= dx
* m_ArrowCos
+ dy
* m_ArrowSin
;
950 p2_y
= -dx
* m_ArrowSin
+ dy
* m_ArrowCos
;
952 //graphics.graphics->DrawLine(&pen, points[0].X,points[0].Y, points[0].X +p1_x,points[0].Y+p1_y);
953 //graphics.graphics->DrawLine(&pen, points[0].X,points[0].Y, points[0].X +p2_x,points[0].Y+p2_y);
957 arrows
[0].X
= points
[0].X
;
958 arrows
[0].Y
= points
[0].Y
;
960 arrows
[1].X
= points
[0].X
+ (REAL
)p1_x
;
961 arrows
[1].Y
= points
[0].Y
+ (REAL
)p1_y
;
963 arrows
[2].X
= points
[0].X
+ (REAL
)dx
*3/5;
964 arrows
[2].Y
= points
[0].Y
+ (REAL
)dy
*3/5;
966 arrows
[3].X
= points
[0].X
+ (REAL
)p2_x
;
967 arrows
[3].Y
= points
[0].Y
+ (REAL
)p2_y
;
969 arrows
[4].X
= points
[0].X
;
970 arrows
[4].Y
= points
[0].Y
;
972 path
.AddLines(arrows
, 5);
973 path
.SetFillMode(FillModeAlternate
);
974 if(graphics
.graphics
)
975 graphics
.graphics
->DrawPath(&pen
, &path
);
976 else if(graphics
.pSVG
)
977 graphics
.pSVG
->DrawPath(arrows
, 5, Color(0,0,0), (int)penwidth
, Color(0,0,0));
981 void CRevisionGraphWnd::DrawTexts (GraphicsDevice
& graphics
, const CRect
& /*logRect*/, const CSize
& offset
)
983 //COLORREF standardTextColor = GetSysColor(COLOR_WINDOWTEXT);
984 if (m_nFontSize
<= 0)
987 // iterate over all visible nodes
990 graphics
.pDC
->SetTextAlign (TA_CENTER
| TA_TOP
);
992 CString fontname
= CAppUtils::GetLogFontName();
994 Gdiplus::Font
font(fontname
, (REAL
)m_nFontSize
, FontStyleRegular
);
995 SolidBrush
blackbrush((ARGB
)Color::Black
);
997 DWORD revGraphUseLocalForCur
= CRegDWORD(L
"Software\\TortoiseGit\\TortoiseProc\\Graph\\RevGraphUseLocalForCur");
1000 forall_nodes(v
,m_Graph
)
1002 // get node and position
1004 String label
=this->m_GraphAttr
.labelNode(v
);
1006 RectF
noderect (GetNodeRect (v
, offset
));
1008 // draw the revision text
1009 CGitHash hash
= this->m_logEntries
[v
->index()];
1010 double hight
= noderect
.Height
/ (!m_HashMap
[hash
].empty() ? m_HashMap
[hash
].size() : 1);
1012 if (m_HashMap
.find(hash
) == m_HashMap
.end() || m_HashMap
[hash
].empty())
1015 background
.SetFromCOLORREF (GetSysColor(COLOR_WINDOW
));
1016 Gdiplus::Pen
pen(background
,1.0F
);
1017 Color brightColor
= LimitedScaleColor (background
, RGB(255,0,0), 0.9f
);
1018 Gdiplus::SolidBrush
brush(brightColor
);
1020 DrawRoundedRect(graphics
, background
,1, &pen
, brightColor
, &brush
, noderect
);
1022 if(graphics
.graphics
)
1024 graphics
.graphics
->DrawString(hash
.ToString().Left(g_Git
.GetShortHASHLength()),-1,
1026 Gdiplus::PointF(noderect
.X
+ this->GetLeftRightMargin()*this->m_fZoomFactor
,noderect
.Y
+this->GetTopBottomMargin()*m_fZoomFactor
),
1031 graphics
.pSVG
->Text((int)(noderect
.X
+ this->GetLeftRightMargin() * this->m_fZoomFactor
),
1032 (int)(noderect
.Y
+ this->GetTopBottomMargin() * m_fZoomFactor
+ m_nFontSize
),
1033 CUnicodeUtils::GetUTF8(fontname
), m_nFontSize
, false, false, (ARGB
)Color::Black
,
1034 CUnicodeUtils::GetUTF8(hash
.ToString().Left(g_Git
.GetShortHASHLength())));
1036 if (graphics
.pGraphviz
)
1038 CString shortHash
= hash
.ToString().Left(g_Git
.GetShortHASHLength());
1039 graphics
.pGraphviz
->DrawNode(L
'g' + shortHash
, shortHash
, fontname
, m_nFontSize
, background
, brightColor
, (int)noderect
.Height
);
1043 if (graphics
.pGraphviz
)
1045 CString id
= L
'g' + hash
.ToString().Left(g_Git
.GetShortHASHLength());
1046 graphics
.pGraphviz
->BeginDrawTableNode(id
, fontname
, m_nFontSize
, (int)noderect
.Height
);
1049 for (size_t i
= 0; i
< m_HashMap
[hash
].size(); ++i
)
1052 CString str
= m_HashMap
[hash
][i
];
1055 rect
.X
= (REAL
)noderect
.X
;
1056 rect
.Y
= (REAL
)(noderect
.Y
+ hight
*i
);
1057 rect
.Width
= (REAL
)noderect
.Width
;
1058 rect
.Height
= (REAL
)hight
;
1060 COLORREF colRef
= RGB(224, 224, 224);
1062 CGit::REF_TYPE refType
;
1063 shortname
= CGit::GetShortName(str
, &refType
);
1066 case CGit::REF_TYPE::LOCAL_BRANCH
:
1067 if (!revGraphUseLocalForCur
&& shortname
== m_CurrentBranch
)
1068 colRef
= m_Colors
.GetColor(CColors::CurrentBranch
);
1070 colRef
= m_Colors
.GetColor(CColors::LocalBranch
);
1072 case CGit::REF_TYPE::REMOTE_BRANCH
:
1073 colRef
= m_Colors
.GetColor(CColors::RemoteBranch
);
1075 case CGit::REF_TYPE::ANNOTATED_TAG
:
1076 case CGit::REF_TYPE::TAG
:
1077 colRef
= m_Colors
.GetColor(CColors::Tag
);
1079 case CGit::REF_TYPE::STASH
:
1080 colRef
= m_Colors
.GetColor(CColors::Stash
);
1082 case CGit::REF_TYPE::BISECT_GOOD
:
1083 colRef
= m_Colors
.GetColor(CColors::BisectGood
);
1085 case CGit::REF_TYPE::BISECT_BAD
:
1086 colRef
= m_Colors
.GetColor(CColors::BisectBad
);
1088 case CGit::REF_TYPE::BISECT_SKIP
:
1089 colRef
= m_Colors
.GetColor(CColors::BisectSkip
);
1091 case CGit::REF_TYPE::NOTES
:
1092 colRef
= m_Colors
.GetColor(CColors::NoteNode
);
1096 Gdiplus::Color
color(GetRValue(colRef
), GetGValue(colRef
), GetBValue(colRef
));
1097 Gdiplus::Pen
pen(color
);
1098 Gdiplus::SolidBrush
brush(color
);
1101 mask
|= (i
==0)? ROUND_UP
:0;
1102 mask
|= (i
== m_HashMap
[hash
].size()-1)? ROUND_DOWN
:0;
1103 this->DrawRoundedRect(graphics
, color
,1,&pen
, color
,&brush
, rect
,mask
);
1105 if (graphics
.graphics
)
1107 //graphics.graphics->FillRectangle(&SolidBrush(Gdiplus::Color(GetRValue(colRef), GetGValue(colRef), GetBValue(colRef))),
1110 graphics
.graphics
->DrawString(shortname
, shortname
.GetLength(),
1112 Gdiplus::PointF((REAL
)(noderect
.X
+ this->GetLeftRightMargin()*m_fZoomFactor
),
1113 (REAL
)(noderect
.Y
+ this->GetTopBottomMargin()*m_fZoomFactor
+ hight
*i
)),
1116 //graphics.graphics->DrawString(shortname.GetBuffer(), shortname.GetLength(), ::new Gdiplus::Font(graphics.pDC->m_hDC), PointF(noderect.X, noderect.Y + hight * i), nullptr, nullptr);
1119 else if (graphics
.pSVG
)
1120 graphics
.pSVG
->Text((int)(noderect
.X
+ this->GetLeftRightMargin() * m_fZoomFactor
),
1121 (int)(noderect
.Y
+ this->GetTopBottomMargin() * m_fZoomFactor
+ hight
* i
+ m_nFontSize
),
1122 CUnicodeUtils::GetUTF8(fontname
), m_nFontSize
,
1123 false, false, (ARGB
)Color::Black
, CUnicodeUtils::GetUTF8(shortname
));
1124 else if (graphics
.pGraphviz
)
1125 graphics
.pGraphviz
->DrawTableNode(shortname
, color
);
1128 if (graphics
.pGraphviz
)
1129 graphics
.pGraphviz
->EndDrawTableNode();
1131 if ((m_SelectedEntry1
== v
))
1132 DrawMarker(graphics
, noderect
, mpLeft
, 0, GetColorFromSysColor(COLOR_HIGHLIGHT
), 1);
1134 if ((m_SelectedEntry2
== v
))
1135 DrawMarker(graphics
, noderect
, mpLeft
, 0, Color(136,0, 21), 2);
1141 void CRevisionGraphWnd::DrawCurrentNodeGlyphs (GraphicsDevice
& graphics
, Image
* glyphs
, const CSize
& offset
)
1143 CSyncPointer
<const ILayoutNodeList
> nodeList (m_state
.GetNodes());
1145 = m_state
.GetOptions()->GetOption
<CUpsideDownLayout
>()->IsActive();
1147 // don't draw glyphs if we are outside the client area
1148 // (e.g. within a scrollbar)
1151 GetCursorPos (&point
);
1152 ScreenToClient (&point
);
1153 if (!GetClientRect().PtInRect (point
))
1156 // expansion glypths etc.
1158 m_hoverIndex
= GetHitNode (point
);
1159 m_hoverGlyphs
= GetHoverGlyphs (point
);
1161 if ((m_hoverIndex
!= NO_INDEX
) || (m_hoverGlyphs
!= 0))
1163 index_t nodeIndex
= m_hoverIndex
== NO_INDEX
1164 ? GetHitNode (point
, CSize (GLYPH_SIZE
, GLYPH_SIZE
/ 2))
1167 if (nodeIndex
>= nodeList
->GetCount())
1170 ILayoutNodeList::SNode node
= nodeList
->GetNode (nodeIndex
);
1171 RectF
noderect (GetNodeRect (node
, offset
));
1173 DWORD flags
= m_state
.GetNodeStates()->GetFlags (node
.node
);
1175 IndicateGlyphDirection (graphics
, nodeList
.get(), node
, noderect
, m_hoverGlyphs
, upsideDown
, offset
);
1176 DrawGlyphs (graphics
, glyphs
, node
.node
, noderect
, flags
, m_hoverGlyphs
, upsideDown
);
1181 void CRevisionGraphWnd::DrawGraph(GraphicsDevice
& graphics
, const CRect
& rect
, int nVScrollPos
, int nHScrollPos
, bool bDirectDraw
)
1183 CMemDC
* memDC
= nullptr;
1188 memDC
= new CMemDC (*graphics
.pDC
, rect
);
1189 graphics
.pDC
= &memDC
->GetDC();
1192 graphics
.pDC
->FillSolidRect(rect
, GetSysColor(COLOR_WINDOW
));
1193 graphics
.pDC
->SetBkMode(TRANSPARENT
);
1196 // preparation & sync
1198 //CSyncPointer<CAllRevisionGraphOptions> options (m_state.GetOptions());
1199 ClearVisibleGlyphs (rect
);
1201 // transform visible
1203 CSize
offset (nHScrollPos
, nVScrollPos
);
1204 CRect
logRect ( (int)(offset
.cx
/ m_fZoomFactor
)-1
1205 , (int)(offset
.cy
/ m_fZoomFactor
)-1
1206 , (int)((rect
.Width() + offset
.cx
) / m_fZoomFactor
) + 1
1207 , (int)((rect
.Height() + offset
.cy
) / m_fZoomFactor
) + 1);
1209 // draw the different components
1213 Graphics
* gcs
= Graphics::FromHDC(*graphics
.pDC
);
1214 graphics
.graphics
= gcs
;
1215 gcs
->SetPageUnit (UnitPixel
);
1216 gcs
->SetInterpolationMode (InterpolationModeHighQualityBicubic
);
1217 gcs
->SetSmoothingMode(SmoothingModeAntiAlias
);
1218 gcs
->SetClip(RectF(Gdiplus::REAL(rect
.left
), Gdiplus::REAL(rect
.top
), Gdiplus::REAL(rect
.Width()), Gdiplus::REAL(rect
.Height())));
1221 // if (options->GetOption<CShowTreeStripes>()->IsActive())
1222 // DrawStripes (graphics, offset);
1224 //if (m_fZoomFactor > SHADOW_ZOOM_THRESHOLD)
1225 // DrawShadows (graphics, logRect, offset);
1227 Bitmap
glyphs (AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_REVGRAPHGLYPHS
));
1229 DrawTexts (graphics
, logRect
, offset
);
1230 DrawConnections (graphics
, logRect
, offset
);
1231 //if (m_showHoverGlyphs)
1232 // DrawCurrentNodeGlyphs (graphics, &glyphs, offset);
1236 if ((!bDirectDraw
)&&(m_Preview
.GetSafeHandle())&&(m_bShowOverview
)&&(graphics
.pDC
))
1238 // draw the overview image rectangle in the top right corner
1239 CMyMemDC
memDC2(graphics
.pDC
, true);
1240 memDC2
.SetWindowOrg(0, 0);
1241 HBITMAP oldhbm
= (HBITMAP
)memDC2
.SelectObject(&m_Preview
);
1242 graphics
.pDC
->BitBlt(rect
.Width()-m_previewWidth
, rect
.Height() - m_previewHeight
, m_previewWidth
, m_previewHeight
,
1243 &memDC2
, 0, 0, SRCCOPY
);
1244 memDC2
.SelectObject(oldhbm
);
1245 // draw the border for the overview rectangle
1246 m_OverviewRect
.left
= rect
.Width()-m_previewWidth
;
1247 m_OverviewRect
.top
= rect
.Height()- m_previewHeight
;
1248 m_OverviewRect
.right
= rect
.Width();
1249 m_OverviewRect
.bottom
= rect
.Height();
1250 graphics
.pDC
->DrawEdge(&m_OverviewRect
, EDGE_BUMP
, BF_RECT
);
1251 // now draw a rectangle where the current view is located in the overview
1253 LONG width
= (long)(rect
.Width() * m_previewZoom
/ m_fZoomFactor
);
1254 LONG height
= (long)(rect
.Height() * m_previewZoom
/ m_fZoomFactor
);
1255 LONG xpos
= (long)(nHScrollPos
* m_previewZoom
/ m_fZoomFactor
);
1256 LONG ypos
= (long)(nVScrollPos
* m_previewZoom
/ m_fZoomFactor
);
1258 tempRect
.left
= rect
.Width()-m_previewWidth
+xpos
;
1259 tempRect
.top
= rect
.Height() - m_previewHeight
+ ypos
;
1260 tempRect
.right
= tempRect
.left
+ width
;
1261 tempRect
.bottom
= tempRect
.top
+ height
;
1262 // make sure the position rect is not bigger than the preview window itself
1263 ::IntersectRect(&m_OverviewPosRect
, &m_OverviewRect
, &tempRect
);
1265 RectF
rect2 ( (float)m_OverviewPosRect
.left
, (float)m_OverviewPosRect
.top
1266 , (float)m_OverviewPosRect
.Width(), (float)m_OverviewPosRect
.Height());
1267 if (graphics
.graphics
)
1269 SolidBrush
brush (Color (64, 0, 0, 0));
1270 graphics
.graphics
->FillRectangle (&brush
, rect2
);
1271 graphics
.pDC
->DrawEdge(&m_OverviewPosRect
, EDGE_BUMP
, BF_RECT
);
1275 // flush changes to screen
1277 delete graphics
.graphics
;
1281 void CRevisionGraphWnd::SetNodeRect(GraphicsDevice
& graphics
, ogdf::node
*pnode
, CGitHash rev
, int mode
)
1283 //multi - line mode. One RefName is one new line
1284 CString fontname
= CAppUtils::GetLogFontName();
1287 if(this->m_HashMap
.find(rev
) == m_HashMap
.end())
1289 CString shorthash
= rev
.ToString().Left(g_Git
.GetShortHASHLength());
1291 if(graphics
.graphics
)
1293 //GetTextExtentPoint32(graphics.pDC->m_hDC, shorthash.GetBuffer(), shorthash.GetLength(), &size);
1294 Gdiplus::Font
font(fontname
, (REAL
)m_nFontSize
, FontStyleRegular
);
1295 graphics
.graphics
->MeasureString(shorthash
, shorthash
.GetLength(),
1297 Gdiplus::PointF(0,0), &rect
);
1299 m_GraphAttr
.width(*pnode
) = this->GetLeftRightMargin()*2 + rect
.Width
;
1300 m_GraphAttr
.height(*pnode
) = this->GetTopBottomMargin()*2 + rect
.Height
;
1307 for (size_t i
= 0; i
< m_HashMap
[rev
].size(); ++i
)
1310 CString shortref
= m_HashMap
[rev
][i
];
1311 shortref
= CGit::GetShortName(shortref
, nullptr);
1314 Gdiplus::Font
font(fontname
, (REAL
)m_nFontSize
, FontStyleRegular
);
1315 graphics
.graphics
->MeasureString(shortref
, shortref
.GetLength(),
1317 Gdiplus::PointF(0,0), &rect
);
1318 if(rect
.Width
> xmax
)
1320 if(rect
.Height
> ymax
)
1325 m_GraphAttr
.width(*pnode
) = this->GetLeftRightMargin()*2 + xmax
;
1326 m_GraphAttr
.height(*pnode
) = (this->GetTopBottomMargin()*2 + ymax
) * lines
;