1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2011 - TortoiseSVN
4 // Copyright (C) 2012-2013 - 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"
24 #include "MessageBox.h"
27 #include "UnicodeUtils.h"
29 //#include "SVNInfo.h"
30 //#include "SVNDiff.h"
31 #include "RevisionGraphWnd.h"
32 //#include "IRevisionGraphLayout.h"
33 //#include "UpsideDownLayout.h"
34 //#include "ShowTreeStripes.h"
36 #include "UnicodeUtils.h"
41 static char THIS_FILE
[] = __FILE__
;
44 using namespace Gdiplus
;
47 /************************************************************************/
48 /* Graphing functions */
49 /************************************************************************/
50 CFont
* CRevisionGraphWnd::GetFont(BOOL bItalic
/*= FALSE*/, BOOL bBold
/*= FALSE*/)
57 if (m_apFonts
[nIndex
] == NULL
)
59 m_apFonts
[nIndex
] = new CFont
;
60 m_lfBaseFont
.lfWeight
= bBold
? FW_BOLD
: FW_NORMAL
;
61 m_lfBaseFont
.lfItalic
= (BYTE
) bItalic
;
62 m_lfBaseFont
.lfStrikeOut
= (BYTE
) FALSE
;
64 m_lfBaseFont
.lfHeight
= -MulDiv(m_nFontSize
, GetDeviceCaps(pDC
->m_hDC
, LOGPIXELSY
), 72);
66 // use the empty font name, so GDI takes the first font which matches
67 // the specs. Maybe this will help render chinese/japanese chars correctly.
68 _tcsncpy_s(m_lfBaseFont
.lfFaceName
, _T("MS Shell Dlg 2"), 32);
69 if (!m_apFonts
[nIndex
]->CreateFontIndirect(&m_lfBaseFont
))
71 delete m_apFonts
[nIndex
];
72 m_apFonts
[nIndex
] = NULL
;
73 return CWnd::GetFont();
76 return m_apFonts
[nIndex
];
79 BOOL
CRevisionGraphWnd::OnEraseBkgnd(CDC
* /*pDC*/)
84 void CRevisionGraphWnd::OnPaint()
87 CPaintDC
dc(this); // device context for painting
88 CRect rect
= GetClientRect();
90 if (IsUpdateJobRunning())
92 CString fetch
= CString(MAKEINTRESOURCE(IDS_PROC_LOADING
));
93 dc
.FillSolidRect(rect
, ::GetSysColor(COLOR_APPWORKSPACE
));
94 dc
.ExtTextOut(20,20,ETO_CLIPPED
,NULL
,fetch
,NULL
);
98 }else if (this->m_Graph
.empty())
100 CString sNoGraphText
;
101 sNoGraphText
.LoadString(IDS_REVGRAPH_ERR_NOGRAPH
);
102 dc
.FillSolidRect(rect
, RGB(255,255,255));
103 dc
.ExtTextOut(20,20,ETO_CLIPPED
,NULL
,sNoGraphText
,NULL
);
109 DrawGraph(dev
, rect
, GetScrollPos(SB_VERT
), GetScrollPos(SB_HORZ
), false);
113 void CRevisionGraphWnd::ClearVisibleGlyphs (const CRect
& /*rect*/)
116 float glyphSize
= GLYPH_SIZE
* m_fZoomFactor
;
118 CSyncPointer
<CRevisionGraphState::TVisibleGlyphs
>
119 visibleGlyphs (m_state
.GetVisibleGlyphs());
121 for (size_t i
= visibleGlyphs
->size(), count
= i
; i
> 0; --i
)
123 const PointF
& leftTop
= (*visibleGlyphs
)[i
-1].leftTop
;
124 CRect
glyphRect ( static_cast<int>(leftTop
.X
)
125 , static_cast<int>(leftTop
.Y
)
126 , static_cast<int>(leftTop
.X
+ glyphSize
)
127 , static_cast<int>(leftTop
.Y
+ glyphSize
));
129 if (CRect().IntersectRect (glyphRect
, rect
))
131 (*visibleGlyphs
)[i
-1] = (*visibleGlyphs
)[--count
];
132 visibleGlyphs
->pop_back();
138 void CRevisionGraphWnd::CutawayPoints (const RectF
& rect
, float cutLen
, TCutRectangle
& result
)
140 result
[0] = PointF (rect
.X
, rect
.Y
+ cutLen
);
141 result
[1] = PointF (rect
.X
+ cutLen
, rect
.Y
);
142 result
[2] = PointF (rect
.GetRight() - cutLen
, rect
.Y
);
143 result
[3] = PointF (rect
.GetRight(), rect
.Y
+ cutLen
);
144 result
[4] = PointF (rect
.GetRight(), rect
.GetBottom() - cutLen
);
145 result
[5] = PointF (rect
.GetRight() - cutLen
, rect
.GetBottom());
146 result
[6] = PointF (rect
.X
+ cutLen
, rect
.GetBottom());
147 result
[7] = PointF (rect
.X
, rect
.GetBottom() - cutLen
);
150 void CRevisionGraphWnd::DrawRoundedRect (GraphicsDevice
& graphics
, const Color
& penColor
, int penWidth
, const Pen
* pen
, const Color
& fillColor
, const Brush
* brush
, const RectF
& rect
, int mask
)
153 enum {POINT_COUNT
= 8};
155 float radius
= CORNER_SIZE
* m_fZoomFactor
;
156 PointF points
[POINT_COUNT
];
157 CutawayPoints (rect
, radius
, points
);
159 if (graphics
.graphics
)
165 path
.AddArc (points
[0].X
, points
[1].Y
, radius
, radius
, 180, 90);
166 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
);
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]);
187 graphics
.graphics
->FillPath (brush
, &path
);
190 graphics
.graphics
->DrawPath (pen
, &path
);
192 else if (graphics
.pSVG
)
194 graphics
.pSVG
->RoundedRectangle((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
, penColor
, penWidth
, fillColor
, (int)radius
, mask
);
199 void CRevisionGraphWnd::DrawOctangle (GraphicsDevice
& graphics
, const Color
& penColor
, int penWidth
, const Pen
* pen
, const Color
& fillColor
, const Brush
* brush
, const RectF
& rect
)
201 enum {POINT_COUNT
= 8};
203 // show left & right edges of low boxes as "<===>"
205 float minCutAway
= min (CORNER_SIZE
* m_fZoomFactor
, rect
.Height
/ 2);
207 // larger boxes: remove 25% of the shorter side
209 float suggestedCutAway
= min (rect
.Height
, rect
.Width
) / 4;
211 // use the more visible one of the former two
213 PointF points
[POINT_COUNT
];
214 CutawayPoints (rect
, max (minCutAway
, suggestedCutAway
), points
);
218 if (graphics
.graphics
)
221 graphics
.graphics
->FillPolygon (brush
, points
, POINT_COUNT
);
223 graphics
.graphics
->DrawPolygon (pen
, points
, POINT_COUNT
);
225 else if (graphics
.pSVG
)
227 graphics
.pSVG
->Polygon(points
, POINT_COUNT
, penColor
, penWidth
, fillColor
);
233 void CRevisionGraphWnd::DrawShape (GraphicsDevice
& graphics
, const Color
& penColor
, int penWidth
, const Pen
* pen
, const Color
& fillColor
, const Brush
* brush
, const RectF
& rect
, NodeShape shape
)
238 if (graphics
.graphics
)
241 graphics
.graphics
->FillRectangle (brush
, rect
);
243 graphics
.graphics
->DrawRectangle (pen
, rect
);
245 else if (graphics
.pSVG
)
247 graphics
.pSVG
->RoundedRectangle((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
, penColor
, penWidth
, fillColor
);
251 DrawRoundedRect (graphics
, penColor
, penWidth
, pen
, fillColor
, brush
, rect
);
254 DrawOctangle (graphics
, penColor
, penWidth
, pen
, fillColor
, brush
, rect
);
257 if (graphics
.graphics
)
260 graphics
.graphics
->FillEllipse (brush
, rect
);
262 graphics
.graphics
->DrawEllipse(pen
, rect
);
264 else if (graphics
.pSVG
)
265 graphics
.pSVG
->Ellipse((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
, penColor
, penWidth
, fillColor
);
268 ASSERT(FALSE
); //unknown type
274 inline BYTE
LimitedScaleColor (BYTE c1
, BYTE c2
, float factor
)
276 BYTE scaled
= c2
+ (BYTE
)((c1
-c2
)*factor
);
282 Color
LimitedScaleColor (const Color
& c1
, const Color
& c2
, float factor
)
284 return Color ( LimitedScaleColor (c1
.GetA(), c2
.GetA(), factor
)
285 , LimitedScaleColor (c1
.GetR(), c2
.GetR(), factor
)
286 , LimitedScaleColor (c1
.GetG(), c2
.GetG(), factor
)
287 , LimitedScaleColor (c1
.GetB(), c2
.GetB(), factor
));
290 inline BYTE
Darken (BYTE c
)
294 : BYTE(int(2*c
) - 0x100);
297 Color
Darken (const Color
& c
)
302 , Darken (c
.GetB()));
305 BYTE
MaxComponentDiff (const Color
& c1
, const Color
& c2
)
307 int rDiff
= abs ((int)c1
.GetR() - (int)c2
.GetR());
308 int gDiff
= abs ((int)c1
.GetG() - (int)c2
.GetG());
309 int bDiff
= abs ((int)c1
.GetB() - (int)c2
.GetB());
311 return (BYTE
) max (max (rDiff
, gDiff
), bDiff
);
315 void CRevisionGraphWnd::DrawShadow (GraphicsDevice
& graphics
, const RectF
& rect
,
316 Color shadowColor
, NodeShape shape
)
321 shadow
.Offset (2, 2);
323 Pen
pen (shadowColor
);
324 SolidBrush
brush (shadowColor
);
326 DrawShape (graphics
, shadowColor
, 1, &pen
, shadowColor
, &brush
, shadow
, shape
);
330 RectF
CRevisionGraphWnd::TransformRectToScreen (const CRect
& rect
, const CSize
& offset
) const
332 PointF
leftTop ( rect
.left
* m_fZoomFactor
333 , rect
.top
* m_fZoomFactor
);
334 return RectF ( leftTop
.X
- offset
.cx
335 , leftTop
.Y
- offset
.cy
336 , rect
.right
* m_fZoomFactor
- leftTop
.X
- 1
337 , rect
.bottom
* m_fZoomFactor
- leftTop
.Y
);
341 RectF
CRevisionGraphWnd::GetNodeRect (const node
& node
, const CSize
& offset
) const
343 // get node and position
346 rect
.left
= (int) (this->m_GraphAttr
.x(node
) - m_GraphAttr
.width(node
)/2);
347 rect
.top
= (int) (this->m_GraphAttr
.y(node
) - m_GraphAttr
.height(node
)/2);
348 rect
.bottom
= (int)( rect
.top
+ m_GraphAttr
.height(node
));
349 rect
.right
= (int)(rect
.left
+ m_GraphAttr
.width(node
));
351 RectF
noderect (TransformRectToScreen (rect
, offset
));
353 // show two separate lines for touching nodes,
354 // unless the scale is too small
356 if (noderect
.Height
> 15.0f
)
357 noderect
.Height
-= 1.0f
;
367 isionGraphWnd::GetBranchCover
368 ( const ILayoutNodeList
* nodeList
371 , const CSize
& offset
)
373 // construct a rect that covers the respective branch
375 CRect
cover (0, 0, 0, 0);
376 while (nodeIndex
!= NO_INDEX
)
378 ILayoutNodeList::SNode node
= nodeList
->GetNode (nodeIndex
);
381 const CVisibleGraphNode
* nextNode
= upward
382 ? node
.node
->GetPrevious()
383 : node
.node
->GetNext();
385 nodeIndex
= nextNode
== NULL
? NO_INDEX
: nextNode
->GetIndex();
388 // expand it just a little to make it look nicer
390 cover
.InflateRect (10, 2, 10, 2);
392 // and now, transfrom it
394 return TransformRectToScreen (cover
, offset
);
399 void CRevisionGraphWnd::DrawShadows (GraphicsDevice
& graphics
, const CRect
& logRect
, const CSize
& offset
)
401 // shadow color to use
404 background
.SetFromCOLORREF (GetSysColor(COLOR_WINDOW
));
406 textColor
.SetFromCOLORREF (GetSysColor(COLOR_WINDOWTEXT
));
408 Color shadowColor
= LimitedScaleColor (background
, ARGB (Color::Black
), 0.5f
);
410 // iterate over all visible nodes
412 CSyncPointer
<const ILayoutNodeList
> nodes (m_state
.GetNodes());
413 for ( index_t index
= nodes
->GetFirstVisible (logRect
)
415 ; index
= nodes
->GetNextVisible (index
, logRect
))
417 // get node and position
419 ILayoutNodeList::SNode node
= nodes
->GetNode (index
);
420 RectF
noderect (GetNodeRect (node
, offset
));
426 case ILayoutNodeList::SNode::STYLE_DELETED
:
427 case ILayoutNodeList::SNode::STYLE_RENAMED
:
428 DrawShadow (graphics
, noderect
, shadowColor
, TSVNOctangle
);
430 case ILayoutNodeList::SNode::STYLE_ADDED
:
431 DrawShadow(graphics
, noderect
, shadowColor
, TSVNRoundRect
);
433 case ILayoutNodeList::SNode::STYLE_LAST
:
434 DrawShadow(graphics
, noderect
, shadowColor
, TSVNEllipse
);
437 DrawShadow(graphics
, noderect
, shadowColor
, TSVNRectangle
);
446 void CRevisionGraphWnd::DrawSquare
447 ( GraphicsDevice
& graphics
448 , const PointF
& leftTop
449 , const Color
& lightColor
450 , const Color
& darkColor
451 , const Color
& penColor
)
453 float squareSize
= MARKER_SIZE
* m_fZoomFactor
;
455 PointF
leftBottom (leftTop
.X
, leftTop
.Y
+ squareSize
);
456 RectF
square (leftTop
, SizeF (squareSize
, squareSize
));
458 if (graphics
.graphics
)
460 LinearGradientBrush
lgBrush (leftTop
, leftBottom
, lightColor
, darkColor
);
461 graphics
.graphics
->FillRectangle (&lgBrush
, square
);
462 if (squareSize
> 4.0f
)
465 graphics
.graphics
->DrawRectangle (&pen
, square
);
468 else if (graphics
.pSVG
)
470 graphics
.pSVG
->GradientRectangle((int)square
.X
, (int)square
.Y
, (int)square
.Width
, (int)square
.Height
,
471 lightColor
, darkColor
, penColor
);
476 void CRevisionGraphWnd::DrawGlyph
477 ( GraphicsDevice
& graphics
479 , const PointF
& leftTop
481 , GlyphPosition position
)
485 if (glyph
== NoGlyph
)
488 // bitmap source area
490 REAL x
= ((REAL
)position
+ (REAL
)glyph
) * GLYPH_BITMAP_SIZE
;
492 // screen target area
494 float glyphSize
= GLYPH_SIZE
* m_fZoomFactor
;
495 RectF
target (leftTop
, SizeF (glyphSize
, glyphSize
));
499 if (graphics
.graphics
)
501 graphics
.graphics
->DrawImage ( glyphs
503 , x
, 0.0f
, GLYPH_BITMAP_SIZE
, GLYPH_BITMAP_SIZE
504 , UnitPixel
, NULL
, NULL
, NULL
);
506 else if (graphics
.pSVG
)
508 // instead of inserting a bitmap, draw a
509 // round rectangle instead.
510 // Embedding images would blow up the resulting
511 // svg file a lot, and the round rectangle
514 // images could be embedded like this:
515 // <image y="100" x="100" id="imgId1234" xlink:href="data:image/png;base64,...base64endodeddata..." height="16" width="16" />
517 graphics
.pSVG
->RoundedRectangle((int)target
.X
, (int)target
.Y
, (int)target
.Width
, (int)target
.Height
,
518 Color(0,0,0), 2, Color(255,255,255), (int)(target
.Width
/3.0));
524 void CRevisionGraphWnd::DrawGlyphs
525 ( GraphicsDevice
& graphics
527 , const CVisibleGraphNode
* node
528 , const PointF
& center
531 , GlyphPosition position
536 // don't show collapse and cut glyths by default
538 if (!showAll
&& ((glyph1
== CollapseGlyph
) || (glyph1
== SplitGlyph
)))
540 if (!showAll
&& ((glyph2
== CollapseGlyph
) || (glyph2
== SplitGlyph
)))
543 // glyth2 shall be set only if 2 glyphs are in use
545 if (glyph1
== NoGlyph
)
547 std::swap (glyph1
, glyph2
);
548 std::swap (state1
, state2
);
553 if (glyph1
== NoGlyph
)
558 CSyncPointer
<CRevisionGraphState::TVisibleGlyphs
>
559 visibleGlyphs (m_state
.GetVisibleGlyphs());
561 float squareSize
= GLYPH_SIZE
* m_fZoomFactor
;
562 if (glyph2
== NoGlyph
)
564 PointF
leftTop (center
.X
- 0.5f
* squareSize
, center
.Y
- 0.5f
* squareSize
);
565 DrawGlyph (graphics
, glyphs
, leftTop
, glyph1
, position
);
566 visibleGlyphs
->push_back
567 (CRevisionGraphState::SVisibleGlyph (state1
, leftTop
, node
));
571 PointF
leftTop1 (center
.X
- squareSize
, center
.Y
- 0.5f
* squareSize
);
572 DrawGlyph (graphics
, glyphs
, leftTop1
, glyph1
, position
);
573 visibleGlyphs
->push_back
574 (CRevisionGraphState::SVisibleGlyph (state1
, leftTop1
, node
));
576 PointF
leftTop2 (center
.X
, center
.Y
- 0.5f
* squareSize
);
577 DrawGlyph (graphics
, glyphs
, leftTop2
, glyph2
, position
);
578 visibleGlyphs
->push_back
579 (CRevisionGraphState::SVisibleGlyph (state2
, leftTop2
, node
));
586 void CRevisionGraphWnd::DrawGlyphs
587 ( GraphicsDevice
& graphics
589 , const CVisibleGraphNode
* node
590 , const RectF
& nodeRect
597 if ((state
== 0) && (allowed
== 0))
602 PointF
topCenter (0.5f
* nodeRect
.GetLeft() + 0.5f
* nodeRect
.GetRight(), nodeRect
.GetTop());
603 PointF
rightCenter (nodeRect
.GetRight(), 0.5f
* nodeRect
.GetTop() + 0.5f
* nodeRect
.GetBottom());
604 PointF
bottomCenter (0.5f
* nodeRect
.GetLeft() + 0.5f
* nodeRect
.GetRight(), nodeRect
.GetBottom());
606 DrawGlyphs ( graphics
609 , upsideDown
? bottomCenter
: topCenter
610 , (state
& CGraphNodeStates::COLLAPSED_ABOVE
) ? ExpandGlyph
: CollapseGlyph
611 , (state
& CGraphNodeStates::SPLIT_ABOVE
) ? JoinGlyph
: SplitGlyph
612 , upsideDown
? Below
: Above
613 , CGraphNodeStates::COLLAPSED_ABOVE
614 , CGraphNodeStates::SPLIT_ABOVE
615 , (allowed
& CGraphNodeStates::COLLAPSED_ABOVE
) != 0);
617 DrawGlyphs ( graphics
621 , (state
& CGraphNodeStates::COLLAPSED_RIGHT
) ? ExpandGlyph
: CollapseGlyph
622 , (state
& CGraphNodeStates::SPLIT_RIGHT
) ? JoinGlyph
: SplitGlyph
624 , CGraphNodeStates::COLLAPSED_RIGHT
625 , CGraphNodeStates::SPLIT_RIGHT
626 , (allowed
& CGraphNodeStates::COLLAPSED_RIGHT
) != 0);
628 DrawGlyphs ( graphics
631 , upsideDown
? topCenter
: bottomCenter
632 , (state
& CGraphNodeStates::COLLAPSED_BELOW
) ? ExpandGlyph
: CollapseGlyph
633 , (state
& CGraphNodeStates::SPLIT_BELOW
) ? JoinGlyph
: SplitGlyph
634 , upsideDown
? Above
: Below
635 , CGraphNodeStates::COLLAPSED_BELOW
636 , CGraphNodeStates::SPLIT_BELOW
637 , (allowed
& CGraphNodeStates::COLLAPSED_BELOW
) != 0);
643 void CRevisionGraphWnd::IndicateGlyphDirection
644 ( GraphicsDevice
& graphics
645 , const ILayoutNodeList
* nodeList
646 , const ILayoutNodeList::SNode
& node
647 , const RectF
& nodeRect
650 , const CSize
& offset
)
657 // where to place the indication?
659 bool indicateAbove
= (glyphs
& CGraphNodeStates::COLLAPSED_ABOVE
) != 0;
660 bool indicateRight
= (glyphs
& CGraphNodeStates::COLLAPSED_RIGHT
) != 0;
661 bool indicateBelow
= (glyphs
& CGraphNodeStates::COLLAPSED_BELOW
) != 0;
663 // fill indication area a semi-transparent blend of red
664 // and the background color
667 color
.SetFromCOLORREF (GetSysColor(COLOR_WINDOW
));
668 color
.SetValue ((color
.GetValue() & 0x807f7f7f) + 0x800000);
670 SolidBrush
brush (color
);
672 // draw the indication (only one condition should match)
674 RectF glyphCenter
= indicateAbove
^ upsideDown
675 ? RectF (nodeRect
.X
, nodeRect
.Y
- 1.0f
, 0.0f
, 0.0f
)
676 : RectF (nodeRect
.X
, nodeRect
.GetBottom() - 1.0f
, 0.0f
, 0.0f
);
680 const CVisibleGraphNode
* firstAffected
= node
.node
->GetSource();
682 assert (firstAffected
);
684 = GetBranchCover (nodeList
, firstAffected
->GetIndex(), true, offset
);
685 RectF::Union (branchCover
, branchCover
, glyphCenter
);
687 if (graphics
.graphics
)
688 graphics
.graphics
->FillRectangle (&brush
, branchCover
);
689 else if (graphics
.pSVG
)
690 graphics
.pSVG
->RoundedRectangle((int)branchCover
.X
, (int)branchCover
.Y
, (int)branchCover
.Width
, (int)branchCover
.Height
,
696 for ( const CVisibleGraphNode::CCopyTarget
* branch
697 = node
.node
->GetFirstCopyTarget()
699 ; branch
= branch
->next())
702 = GetBranchCover (nodeList
, branch
->value()->GetIndex(), false, offset
);
703 if (graphics
.graphics
)
704 graphics
.graphics
->FillRectangle (&brush
, branchCover
);
705 else if (graphics
.pSVG
)
706 graphics
.pSVG
->RoundedRectangle((int)branchCover
.X
, (int)branchCover
.Y
, (int)branchCover
.Width
, (int)branchCover
.Height
,
713 const CVisibleGraphNode
* firstAffected
714 = node
.node
->GetNext();
717 = GetBranchCover (nodeList
, firstAffected
->GetIndex(), false, offset
);
718 RectF::Union (branchCover
, branchCover
, glyphCenter
);
720 if (graphics
.graphics
)
721 graphics
.graphics
->FillRectangle (&brush
, branchCover
);
722 else if (graphics
.pSVG
)
723 graphics
.pSVG
->RoundedRectangle((int)branchCover
.X
, (int)branchCover
.Y
, (int)branchCover
.Width
, (int)branchCover
.Height
,
730 void CRevisionGraphWnd::DrawMarker
731 ( GraphicsDevice
& graphics
732 , const RectF
& noderect
733 , MarkerPosition
/*position*/
734 , int /*relPosition*/
735 , const Color
& penColor
738 REAL width
= 4*this->m_fZoomFactor
<1? 1: 4*this->m_fZoomFactor
;
739 Pen
pen(penColor
,width
);
740 DrawRoundedRect(graphics
, penColor
, (int)width
, &pen
, Color(0,0,0), NULL
, noderect
);
744 REAL x
= max(1, 10 * this->m_fZoomFactor
);
745 REAL y1
= max(1, 25 * this->m_fZoomFactor
);
746 REAL y2
= max(1, 5 * this->m_fZoomFactor
);
747 if(graphics
.graphics
)
748 graphics
.graphics
->DrawLine(&pen
, noderect
.X
+ x
, noderect
.Y
- y1
, noderect
.X
+ x
, noderect
.Y
- y2
);
753 REAL x1
= max(1, 5 * this->m_fZoomFactor
);
754 REAL x2
= max(1, 15 * this->m_fZoomFactor
);
755 REAL y1
= max(1, 25 * this->m_fZoomFactor
);
756 REAL y2
= max(1, 5 * this->m_fZoomFactor
);
757 if(graphics
.graphics
)
759 graphics
.graphics
->DrawLine(&pen
, noderect
.X
+ x1
, noderect
.Y
- y1
, noderect
.X
+ x1
, noderect
.Y
- y2
);
760 graphics
.graphics
->DrawLine(&pen
, noderect
.X
+ x2
, noderect
.Y
- y1
, noderect
.X
+ x2
, noderect
.Y
- y2
);
766 void CRevisionGraphWnd::DrawStripes (GraphicsDevice
& graphics
, const CSize
& offset
)
768 // we need to fill this visible area of the the screen
769 // (even if there is graph in that part)
772 if (graphics
.graphics
)
773 graphics
.graphics
->GetVisibleClipBounds (&clipRect
);
775 // don't show stripes if we don't have multiple roots
777 CSyncPointer
<const ILayoutRectList
> trees (m_state
.GetTrees());
778 if (trees
->GetCount() < 2)
781 // iterate over all trees
783 for ( index_t i
= 0, count
= trees
->GetCount(); i
< count
; ++i
)
785 // screen coordinates covered by the tree
787 CRect tree
= trees
->GetRect(i
);
788 REAL left
= tree
.left
* m_fZoomFactor
;
789 REAL right
= tree
.right
* m_fZoomFactor
;
790 RectF
rect ( left
- offset
.cx
792 , i
+1 == count
? clipRect
.Width
: right
- left
797 if (rect
.IntersectsWith (clipRect
))
799 // draw the background stripe
801 Color
color ( (i
& 1) == 0
802 ? m_Colors
.GetColor (CColors::gdpStripeColor1
)
803 : m_Colors
.GetColor (CColors::gdpStripeColor2
));
804 if (graphics
.graphics
)
806 SolidBrush
brush (color
);
807 graphics
.graphics
->FillRectangle (&brush
, rect
);
809 else if (graphics
.pSVG
)
810 graphics
.pSVG
->RoundedRectangle((int)rect
.X
, (int)rect
.Y
, (int)rect
.Width
, (int)rect
.Height
,
817 PointF
CRevisionGraphWnd::cutPoint(node v
,double lw
,PointF ps
, PointF pt
)
819 double x
= m_GraphAttr
.x(v
);
820 double y
= m_GraphAttr
.y(v
);
821 double xmin
= x
- this->m_GraphAttr
.width(v
)/2 - lw
/2;
822 double xmax
= x
+ this->m_GraphAttr
.width(v
)/2 + lw
/2;
823 double ymin
= y
- this->m_GraphAttr
.height(v
)/2 - lw
/2;
824 double ymax
= y
+ this->m_GraphAttr
.height(v
)/2 + lw
/2;;
826 double dx
= pt
.X
- ps
.X
;
827 double dy
= pt
.Y
- ps
.Y
;
832 double t
= (ymax
-ps
.Y
) / dy
;
833 double x
= ps
.X
+ t
*dx
;
835 if(xmin
<= x
&& x
<= xmax
)
836 return PointF((REAL
)x
, (REAL
)ymax
);
839 } else if(pt
.Y
< ymin
) {
840 double t
= (ymin
-ps
.Y
) / dy
;
841 double x
= ps
.X
+ t
*dx
;
843 if(xmin
<= x
&& x
<= xmax
)
844 return PointF((REAL
)x
, (REAL
)ymin
);
852 double t
= (xmax
-ps
.X
) / dx
;
853 double y
= ps
.Y
+ t
*dy
;
855 if(ymin
<= y
&& y
<= ymax
)
856 return PointF((REAL
)xmax
, (REAL
)y
);
859 } else if(pt
.X
< xmin
) {
860 double t
= (xmin
-ps
.X
) / dx
;
861 double y
= ps
.Y
+ t
*dy
;
863 if(ymin
<= y
&& y
<= ymax
)
864 return PointF((REAL
)xmin
, (REAL
)y
);
873 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(Color(0,0,0),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
)
928 graphics
.graphics
->DrawLines(&pen
, points
.GetData(), (INT
)points
.GetCount());
931 else if (graphics
.pSVG
)
934 color
.SetFromCOLORREF(GetSysColor(COLOR_WINDOWTEXT
));
935 graphics
.pSVG
->Polyline(points
.GetData(), (int)points
.GetCount(), Color(0,0,0), (int)penwidth
);
937 else if (graphics
.pGraphviz
)
939 CString hash1
= _T("g") + m_logEntries
[e
->target()->index()].ToString().Left(g_Git
.GetShortHASHLength());
940 CString hash2
= _T("g") + m_logEntries
[e
->source()->index()].ToString().Left(g_Git
.GetShortHASHLength());
941 graphics
.pGraphviz
->DrawEdge(hash1
, hash2
);
945 double dx
= points
[1].X
- points
[0].X
;
946 double dy
= points
[1].Y
- points
[0].Y
;
948 double len
= sqrt(dx
*dx
+ dy
*dy
);
949 dx
= m_ArrowSize
* m_fZoomFactor
*dx
/len
;
950 dy
= m_ArrowSize
* m_fZoomFactor
*dy
/len
;
952 double p1_x
, p1_y
, p2_x
, p2_y
;
953 p1_x
= dx
* m_ArrowCos
- dy
* m_ArrowSin
;
954 p1_y
= dx
* m_ArrowSin
+ dy
* m_ArrowCos
;
956 p2_x
= dx
* m_ArrowCos
+ dy
* m_ArrowSin
;
957 p2_y
= -dx
* m_ArrowSin
+ dy
* m_ArrowCos
;
959 //graphics.graphics->DrawLine(&pen, points[0].X,points[0].Y, points[0].X +p1_x,points[0].Y+p1_y);
960 //graphics.graphics->DrawLine(&pen, points[0].X,points[0].Y, points[0].X +p2_x,points[0].Y+p2_y);
964 arrows
[0].X
= points
[0].X
;
965 arrows
[0].Y
= points
[0].Y
;
967 arrows
[1].X
= points
[0].X
+ (REAL
)p1_x
;
968 arrows
[1].Y
= points
[0].Y
+ (REAL
)p1_y
;
970 arrows
[2].X
= points
[0].X
+ (REAL
)dx
*3/5;
971 arrows
[2].Y
= points
[0].Y
+ (REAL
)dy
*3/5;
973 arrows
[3].X
= points
[0].X
+ (REAL
)p2_x
;
974 arrows
[3].Y
= points
[0].Y
+ (REAL
)p2_y
;
976 arrows
[4].X
= points
[0].X
;
977 arrows
[4].Y
= points
[0].Y
;
979 path
.AddLines(arrows
, 5);
980 path
.SetFillMode(FillModeAlternate
);
981 if(graphics
.graphics
)
983 graphics
.graphics
->DrawPath(&pen
, &path
);
984 }else if(graphics
.pSVG
)
986 graphics
.pSVG
->DrawPath(arrows
, 5, Color(0,0,0), (int)penwidth
, Color(0,0,0));
993 void CRevisionGraphWnd::DrawTexts (GraphicsDevice
& graphics
, const CRect
& /*logRect*/, const CSize
& offset
)
995 //COLORREF standardTextColor = GetSysColor(COLOR_WINDOWTEXT);
996 if (m_nFontSize
<= 0)
999 // iterate over all visible nodes
1002 graphics
.pDC
->SetTextAlign (TA_CENTER
| TA_TOP
);
1005 CString fontname
= CRegString(_T("Software\\TortoiseGit\\LogFontName"), _T("Courier New"));
1007 Gdiplus::Font
font(fontname
.GetBuffer(),(REAL
)m_nFontSize
,FontStyleRegular
);
1008 SolidBrush
blackbrush((ARGB
)Color::Black
);
1010 DWORD revGraphUseLocalForCur
= CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\Graph\\RevGraphUseLocalForCur"));
1013 forall_nodes(v
,m_Graph
)
1015 // get node and position
1017 String label
=this->m_GraphAttr
.labelNode(v
);
1019 RectF
noderect (GetNodeRect (v
, offset
));
1021 // draw the revision text
1022 CGitHash hash
= this->m_logEntries
[v
->index()];
1023 double hight
= noderect
.Height
/ (m_HashMap
[hash
].size()?m_HashMap
[hash
].size():1);
1025 if(m_HashMap
.find(hash
) == m_HashMap
.end() || m_HashMap
[hash
].size() == 0)
1028 background
.SetFromCOLORREF (GetSysColor(COLOR_WINDOW
));
1029 Gdiplus::Pen
pen(background
,1.0F
);
1030 Color brightColor
= LimitedScaleColor (background
, RGB(255,0,0), 0.9f
);
1031 Gdiplus::SolidBrush
brush(brightColor
);
1033 DrawRoundedRect(graphics
, background
,1, &pen
, brightColor
, &brush
, noderect
);
1035 if(graphics
.graphics
)
1037 graphics
.graphics
->DrawString(hash
.ToString().Left(g_Git
.GetShortHASHLength()),-1,
1039 Gdiplus::PointF(noderect
.X
+ this->GetLeftRightMargin()*this->m_fZoomFactor
,noderect
.Y
+this->GetTopBottomMargin()*m_fZoomFactor
),
1044 graphics
.pSVG
->Text((int)(noderect
.X
+ this->GetLeftRightMargin() * this->m_fZoomFactor
),
1045 (int)(noderect
.Y
+ this->GetTopBottomMargin() * m_fZoomFactor
+ m_nFontSize
),
1046 CUnicodeUtils::GetUTF8(fontname
), m_nFontSize
, false, false, (ARGB
)Color::Black
,
1047 CUnicodeUtils::GetUTF8(hash
.ToString().Left(g_Git
.GetShortHASHLength())));
1049 if (graphics
.pGraphviz
)
1051 CString shortHash
= hash
.ToString().Left(g_Git
.GetShortHASHLength());
1052 graphics
.pGraphviz
->DrawNode(_T("g") + shortHash
, shortHash
, fontname
, m_nFontSize
, background
, brightColor
, (int)noderect
.Height
);
1056 if (graphics
.pGraphviz
)
1058 CString id
= _T("g") + hash
.ToString().Left(g_Git
.GetShortHASHLength());
1059 graphics
.pGraphviz
->BeginDrawTableNode(id
, fontname
, m_nFontSize
, (int)noderect
.Height
);
1062 for (size_t i
= 0; i
< m_HashMap
[hash
].size(); ++i
)
1065 CString str
= m_HashMap
[hash
][i
];
1068 rect
.X
= (REAL
)noderect
.X
;
1069 rect
.Y
= (REAL
)(noderect
.Y
+ hight
*i
);
1070 rect
.Width
= (REAL
)noderect
.Width
;
1071 rect
.Height
= (REAL
)hight
;
1073 COLORREF colRef
= RGB(224, 224, 224);
1076 if(CGit::GetShortName(str
,shortname
,_T("refs/heads/")))
1078 if (!revGraphUseLocalForCur
&& shortname
== m_CurrentBranch
)
1079 colRef
= m_Colors
.GetColor(CColors::CurrentBranch
);
1081 colRef
= m_Colors
.GetColor(CColors::LocalBranch
);
1084 else if(CGit::GetShortName(str
,shortname
,_T("refs/remotes/")))
1086 colRef
= m_Colors
.GetColor(CColors::RemoteBranch
);
1088 else if(CGit::GetShortName(str
,shortname
,_T("refs/tags/")))
1090 colRef
= m_Colors
.GetColor(CColors::Tag
);
1092 else if(CGit::GetShortName(str
,shortname
,_T("refs/stash")))
1094 colRef
= m_Colors
.GetColor(CColors::Stash
);
1095 shortname
=_T("stash");
1097 else if(CGit::GetShortName(str
,shortname
,_T("refs/bisect/")))
1099 if(shortname
.Find(_T("good")) == 0)
1101 colRef
= m_Colors
.GetColor(CColors::BisectGood
);
1102 shortname
= _T("good");
1105 if(shortname
.Find(_T("bad")) == 0)
1107 colRef
= m_Colors
.GetColor(CColors::BisectBad
);
1108 shortname
= _T("bad");
1110 }else if(CGit::GetShortName(str
,shortname
,_T("refs/notes/")))
1112 colRef
= m_Colors
.GetColor(CColors::NoteNode
);
1116 CGit::GetShortName(str
, shortname
, _T("refs/"));
1119 Gdiplus::Color
color(GetRValue(colRef
), GetGValue(colRef
), GetBValue(colRef
));
1120 Gdiplus::Pen
pen(color
);
1121 Gdiplus::SolidBrush
brush(color
);
1124 mask
|= (i
==0)? ROUND_UP
:0;
1125 mask
|= (i
== m_HashMap
[hash
].size()-1)? ROUND_DOWN
:0;
1126 this->DrawRoundedRect(graphics
, color
,1,&pen
, color
,&brush
, rect
,mask
);
1128 if (graphics
.graphics
)
1131 //graphics.graphics->FillRectangle(&SolidBrush(Gdiplus::Color(GetRValue(colRef), GetGValue(colRef), GetBValue(colRef))),
1134 graphics
.graphics
->DrawString(shortname
.GetBuffer(),shortname
.GetLength(),
1136 Gdiplus::PointF((REAL
)(noderect
.X
+ this->GetLeftRightMargin()*m_fZoomFactor
),
1137 (REAL
)(noderect
.Y
+ this->GetTopBottomMargin()*m_fZoomFactor
+ hight
*i
)),
1140 //graphics.graphics->DrawString(shortname.GetBuffer(), shortname.GetLength(), ::new Gdiplus::Font(graphics.pDC->m_hDC), PointF(noderect.X, noderect.Y + hight *i),NULL, NULL);
1143 else if (graphics
.pSVG
)
1146 graphics
.pSVG
->Text((int)(noderect
.X
+ this->GetLeftRightMargin() * m_fZoomFactor
),
1147 (int)(noderect
.Y
+ this->GetTopBottomMargin() * m_fZoomFactor
+ hight
* i
+ m_nFontSize
),
1148 CUnicodeUtils::GetUTF8(fontname
), m_nFontSize
,
1149 false, false, (ARGB
)Color::Black
, CUnicodeUtils::GetUTF8(shortname
));
1152 else if (graphics
.pGraphviz
)
1154 graphics
.pGraphviz
->DrawTableNode(shortname
, color
);
1158 if (graphics
.pGraphviz
)
1160 graphics
.pGraphviz
->EndDrawTableNode();
1163 if ((m_SelectedEntry1
== v
))
1164 DrawMarker(graphics
, noderect
, mpLeft
, 0, Color(0,0, 255), 1);
1166 if ((m_SelectedEntry2
== v
))
1167 DrawMarker(graphics
, noderect
, mpLeft
, 0, Color(136,0, 21), 2);
1174 void CRevisionGraphWnd::DrawCurrentNodeGlyphs (GraphicsDevice
& graphics
, Image
* glyphs
, const CSize
& offset
)
1177 CSyncPointer
<const ILayoutNodeList
> nodeList (m_state
.GetNodes());
1179 = m_state
.GetOptions()->GetOption
<CUpsideDownLayout
>()->IsActive();
1181 // don't draw glyphs if we are outside the client area
1182 // (e.g. within a scrollbar)
1185 GetCursorPos (&point
);
1186 ScreenToClient (&point
);
1187 if (!GetClientRect().PtInRect (point
))
1190 // expansion glypths etc.
1192 m_hoverIndex
= GetHitNode (point
);
1193 m_hoverGlyphs
= GetHoverGlyphs (point
);
1195 if ((m_hoverIndex
!= NO_INDEX
) || (m_hoverGlyphs
!= 0))
1197 index_t nodeIndex
= m_hoverIndex
== NO_INDEX
1198 ? GetHitNode (point
, CSize (GLYPH_SIZE
, GLYPH_SIZE
/ 2))
1201 if (nodeIndex
>= nodeList
->GetCount())
1204 ILayoutNodeList::SNode node
= nodeList
->GetNode (nodeIndex
);
1205 RectF
noderect (GetNodeRect (node
, offset
));
1207 DWORD flags
= m_state
.GetNodeStates()->GetFlags (node
.node
);
1209 IndicateGlyphDirection (graphics
, nodeList
.get(), node
, noderect
, m_hoverGlyphs
, upsideDown
, offset
);
1210 DrawGlyphs (graphics
, glyphs
, node
.node
, noderect
, flags
, m_hoverGlyphs
, upsideDown
);
1216 void CRevisionGraphWnd::DrawGraph(GraphicsDevice
& graphics
, const CRect
& rect
, int nVScrollPos
, int nHScrollPos
, bool bDirectDraw
)
1218 CMemDC
* memDC
= NULL
;
1223 memDC
= new CMemDC (*graphics
.pDC
, rect
);
1224 graphics
.pDC
= &memDC
->GetDC();
1227 graphics
.pDC
->FillSolidRect(rect
, GetSysColor(COLOR_WINDOW
));
1228 graphics
.pDC
->SetBkMode(TRANSPARENT
);
1231 // preparation & sync
1233 //CSyncPointer<CAllRevisionGraphOptions> options (m_state.GetOptions());
1234 ClearVisibleGlyphs (rect
);
1236 // transform visible
1238 CSize
offset (nHScrollPos
, nVScrollPos
);
1239 CRect
logRect ( (int)(offset
.cx
/ m_fZoomFactor
)-1
1240 , (int)(offset
.cy
/ m_fZoomFactor
)-1
1241 , (int)((rect
.Width() + offset
.cx
) / m_fZoomFactor
) + 1
1242 , (int)((rect
.Height() + offset
.cy
) / m_fZoomFactor
) + 1);
1244 // draw the different components
1248 Graphics
* gcs
= Graphics::FromHDC(*graphics
.pDC
);
1249 graphics
.graphics
= gcs
;
1250 gcs
->SetPageUnit (UnitPixel
);
1251 gcs
->SetInterpolationMode (InterpolationModeHighQualityBicubic
);
1252 gcs
->SetSmoothingMode(SmoothingModeAntiAlias
);
1253 gcs
->SetClip(RectF(Gdiplus::REAL(rect
.left
), Gdiplus::REAL(rect
.top
), Gdiplus::REAL(rect
.Width()), Gdiplus::REAL(rect
.Height())));
1256 // if (options->GetOption<CShowTreeStripes>()->IsActive())
1257 // DrawStripes (graphics, offset);
1259 //if (m_fZoomFactor > SHADOW_ZOOM_THRESHOLD)
1260 // DrawShadows (graphics, logRect, offset);
1262 Bitmap
glyphs (AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_REVGRAPHGLYPHS
));
1264 DrawTexts (graphics
, logRect
, offset
);
1265 DrawConnections (graphics
, logRect
, offset
);
1266 //if (m_showHoverGlyphs)
1267 // DrawCurrentNodeGlyphs (graphics, &glyphs, offset);
1271 if ((!bDirectDraw
)&&(m_Preview
.GetSafeHandle())&&(m_bShowOverview
)&&(graphics
.pDC
))
1273 // draw the overview image rectangle in the top right corner
1274 CMyMemDC
memDC2(graphics
.pDC
, true);
1275 memDC2
.SetWindowOrg(0, 0);
1276 HBITMAP oldhbm
= (HBITMAP
)memDC2
.SelectObject(&m_Preview
);
1277 graphics
.pDC
->BitBlt(rect
.Width()-m_previewWidth
, rect
.Height() - m_previewHeight
, m_previewWidth
, m_previewHeight
,
1278 &memDC2
, 0, 0, SRCCOPY
);
1279 memDC2
.SelectObject(oldhbm
);
1280 // draw the border for the overview rectangle
1281 m_OverviewRect
.left
= rect
.Width()-m_previewWidth
;
1282 m_OverviewRect
.top
= rect
.Height()- m_previewHeight
;
1283 m_OverviewRect
.right
= rect
.Width();
1284 m_OverviewRect
.bottom
= rect
.Height();
1285 graphics
.pDC
->DrawEdge(&m_OverviewRect
, EDGE_BUMP
, BF_RECT
);
1286 // now draw a rectangle where the current view is located in the overview
1288 CRect viewRect
= GetViewRect();
1289 LONG width
= (long)(rect
.Width() * m_previewZoom
/ m_fZoomFactor
);
1290 LONG height
= (long)(rect
.Height() * m_previewZoom
/ m_fZoomFactor
);
1291 LONG xpos
= (long)(nHScrollPos
* m_previewZoom
/ m_fZoomFactor
);
1292 LONG ypos
= (long)(nVScrollPos
* m_previewZoom
/ m_fZoomFactor
);
1294 tempRect
.left
= rect
.Width()-m_previewWidth
+xpos
;
1295 tempRect
.top
= rect
.Height() - m_previewHeight
+ ypos
;
1296 tempRect
.right
= tempRect
.left
+ width
;
1297 tempRect
.bottom
= tempRect
.top
+ height
;
1298 // make sure the position rect is not bigger than the preview window itself
1299 ::IntersectRect(&m_OverviewPosRect
, &m_OverviewRect
, &tempRect
);
1301 RectF
rect2 ( (float)m_OverviewPosRect
.left
, (float)m_OverviewPosRect
.top
1302 , (float)m_OverviewPosRect
.Width(), (float)m_OverviewPosRect
.Height());
1303 if (graphics
.graphics
)
1305 SolidBrush
brush (Color (64, 0, 0, 0));
1306 graphics
.graphics
->FillRectangle (&brush
, rect2
);
1307 graphics
.pDC
->DrawEdge(&m_OverviewPosRect
, EDGE_BUMP
, BF_RECT
);
1311 // flush changes to screen
1313 delete graphics
.graphics
;
1317 void CRevisionGraphWnd::SetNodeRect(GraphicsDevice
& graphics
, ogdf::node
*pnode
, CGitHash rev
, int mode
)
1319 //multi - line mode. One RefName is one new line
1320 CString fontname
= CRegString(_T("Software\\TortoiseGit\\LogFontName"), _T("Courier New"));
1323 if(this->m_HashMap
.find(rev
) == m_HashMap
.end())
1325 CString shorthash
= rev
.ToString().Left(g_Git
.GetShortHASHLength());
1327 if(graphics
.graphics
)
1329 //GetTextExtentPoint32(graphics.pDC->m_hDC, shorthash.GetBuffer(), shorthash.GetLength(), &size);
1330 Gdiplus::Font
font(fontname
.GetBuffer(), (REAL
)m_nFontSize
, FontStyleRegular
);
1331 graphics
.graphics
->MeasureString(shorthash
.GetBuffer(), shorthash
.GetLength(),
1333 Gdiplus::PointF(0,0), &rect
);
1336 m_GraphAttr
.width(*pnode
) = this->GetLeftRightMargin()*2 + rect
.Width
;
1337 m_GraphAttr
.height(*pnode
) = this->GetTopBottomMargin()*2 + rect
.Height
;
1344 for (size_t i
= 0; i
< m_HashMap
[rev
].size(); ++i
)
1347 CString shortref
= m_HashMap
[rev
][i
];
1348 shortref
= CGit::GetShortName(shortref
,NULL
);
1351 Gdiplus::Font
font(fontname
.GetBuffer(), (REAL
)m_nFontSize
, FontStyleRegular
);
1352 graphics
.graphics
->MeasureString(shortref
.GetBuffer(), shortref
.GetLength(),
1354 Gdiplus::PointF(0,0), &rect
);
1355 if(rect
.Width
> xmax
)
1357 if(rect
.Height
> ymax
)
1362 m_GraphAttr
.width(*pnode
) = this->GetLeftRightMargin()*2 + xmax
;
1363 m_GraphAttr
.height(*pnode
) = (this->GetTopBottomMargin()*2 + ymax
) * lines
;