Merge branch 'git-describe'
[TortoiseGit.git] / src / TortoiseProc / RevisionGraph / RevisionGraphDlgDraw.cpp
blob401f527d4f2b40042cadc2768899935efcf6f79a
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.
20 #include "stdafx.h"
21 #include "TortoiseProc.h"
22 #include "MyMemDC.h"
23 #include "RevisionGraphDlg.h"
24 #include "MessageBox.h"
25 #include "Git.h"
26 #include "TempFile.h"
27 #include "UnicodeUtils.h"
28 #include "TGitPath.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"
35 #include "registry.h"
36 #include "UnicodeUtils.h"
38 #ifdef _DEBUG
39 #define new DEBUG_NEW
40 #undef THIS_FILE
41 static char THIS_FILE[] = __FILE__;
42 #endif
44 using namespace Gdiplus;
45 using namespace ogdf;
47 /************************************************************************/
48 /* Graphing functions */
49 /************************************************************************/
50 CFont* CRevisionGraphWnd::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/)
52 int nIndex = 0;
53 if (bBold)
54 nIndex |= 1;
55 if (bItalic)
56 nIndex |= 2;
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;
63 CDC * pDC = GetDC();
64 m_lfBaseFont.lfHeight = -MulDiv(m_nFontSize, GetDeviceCaps(pDC->m_hDC, LOGPIXELSY), 72);
65 ReleaseDC(pDC);
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*/)
81 return TRUE;
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);
95 CWnd::OnPaint();
96 return;
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);
104 return;
107 GraphicsDevice dev;
108 dev.pDC = &dc;
109 DrawGraph(dev, rect, GetScrollPos(SB_VERT), GetScrollPos(SB_HORZ), false);
113 void CRevisionGraphWnd::ClearVisibleGlyphs (const CRect& /*rect*/)
115 #if 0
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();
135 #endif
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)
161 GraphicsPath path;
163 if(mask & ROUND_UP)
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);
167 }else
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);
176 }else
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]);
185 if (brush != NULL)
187 graphics.graphics->FillPath (brush, &path);
189 if (pen != NULL)
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);
216 // now, draw it
218 if (graphics.graphics)
220 if (brush != NULL)
221 graphics.graphics->FillPolygon (brush, points, POINT_COUNT);
222 if (pen != NULL)
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)
235 switch( shape )
237 case TSVNRectangle:
238 if (graphics.graphics)
240 if (brush != NULL)
241 graphics.graphics->FillRectangle (brush, rect);
242 if (pen != NULL)
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);
249 break;
250 case TSVNRoundRect:
251 DrawRoundedRect (graphics, penColor, penWidth, pen, fillColor, brush, rect);
252 break;
253 case TSVNOctangle:
254 DrawOctangle (graphics, penColor, penWidth, pen, fillColor, brush, rect);
255 break;
256 case TSVNEllipse:
257 if (graphics.graphics)
259 if (brush != NULL)
260 graphics.graphics->FillEllipse (brush, rect);
261 if (pen != NULL)
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);
266 break;
267 default:
268 ASSERT(FALSE); //unknown type
269 return;
274 inline BYTE LimitedScaleColor (BYTE c1, BYTE c2, float factor)
276 BYTE scaled = c2 + (BYTE)((c1-c2)*factor);
277 return c1 < c2
278 ? max (c1, scaled)
279 : min (c1, scaled);
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)
292 return c < 0xc0
293 ? (c / 3) * 2
294 : BYTE(int(2*c) - 0x100);
297 Color Darken (const Color& c)
299 return Color ( 0xff
300 , Darken (c.GetR())
301 , Darken (c.GetG())
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);
314 #if 0
315 void CRevisionGraphWnd::DrawShadow (GraphicsDevice& graphics, const RectF& rect,
316 Color shadowColor, NodeShape shape)
318 // draw the shadow
320 RectF shadow = rect;
321 shadow.Offset (2, 2);
323 Pen pen (shadowColor);
324 SolidBrush brush (shadowColor);
326 DrawShape (graphics, shadowColor, 1, &pen, shadowColor, &brush, shadow, shape);
328 #endif
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
345 CRect rect;
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;
359 // done
361 return noderect;
365 #if 0
366 RectF CRev
367 isionGraphWnd::GetBranchCover
368 ( const ILayoutNodeList* nodeList
369 , index_t nodeIndex
370 , bool upward
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);
379 cover |= node.rect;
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);
396 #endif
398 #if 0
399 void CRevisionGraphWnd::DrawShadows (GraphicsDevice& graphics, const CRect& logRect, const CSize& offset)
401 // shadow color to use
403 Color background;
404 background.SetFromCOLORREF (GetSysColor(COLOR_WINDOW));
405 Color textColor;
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)
414 ; index != NO_INDEX
415 ; index = nodes->GetNextVisible (index, logRect))
417 // get node and position
419 ILayoutNodeList::SNode node = nodes->GetNode (index);
420 RectF noderect (GetNodeRect (node, offset));
422 // actual drawing
424 switch (node.style)
426 case ILayoutNodeList::SNode::STYLE_DELETED:
427 case ILayoutNodeList::SNode::STYLE_RENAMED:
428 DrawShadow (graphics, noderect, shadowColor, TSVNOctangle);
429 break;
430 case ILayoutNodeList::SNode::STYLE_ADDED:
431 DrawShadow(graphics, noderect, shadowColor, TSVNRoundRect);
432 break;
433 case ILayoutNodeList::SNode::STYLE_LAST:
434 DrawShadow(graphics, noderect, shadowColor, TSVNEllipse);
435 break;
436 default:
437 DrawShadow(graphics, noderect, shadowColor, TSVNRectangle);
438 break;
443 #endif
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)
464 Pen pen (penColor);
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);
475 #if 0
476 void CRevisionGraphWnd::DrawGlyph
477 ( GraphicsDevice& graphics
478 , Image* glyphs
479 , const PointF& leftTop
480 , GlyphType glyph
481 , GlyphPosition position)
483 // special case
485 if (glyph == NoGlyph)
486 return;
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));
497 // scaled copy
499 if (graphics.graphics)
501 graphics.graphics->DrawImage ( glyphs
502 , target
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
512 // is enough IMHO.
513 // Note:
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));
521 #endif
523 #if 0
524 void CRevisionGraphWnd::DrawGlyphs
525 ( GraphicsDevice& graphics
526 , Image* glyphs
527 , const CVisibleGraphNode* node
528 , const PointF& center
529 , GlyphType glyph1
530 , GlyphType glyph2
531 , GlyphPosition position
532 , DWORD state1
533 , DWORD state2
534 , bool showAll)
536 // don't show collapse and cut glyths by default
538 if (!showAll && ((glyph1 == CollapseGlyph) || (glyph1 == SplitGlyph)))
539 glyph1 = NoGlyph;
540 if (!showAll && ((glyph2 == CollapseGlyph) || (glyph2 == SplitGlyph)))
541 glyph2 = NoGlyph;
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);
551 // anything to do?
553 if (glyph1 == NoGlyph)
554 return;
556 // 1 or 2 glyphs?
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));
569 else
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));
583 #endif
585 #if 0
586 void CRevisionGraphWnd::DrawGlyphs
587 ( GraphicsDevice& graphics
588 , Image* glyphs
589 , const CVisibleGraphNode* node
590 , const RectF& nodeRect
591 , DWORD state
592 , DWORD allowed
593 , bool upsideDown)
595 // shortcut
597 if ((state == 0) && (allowed == 0))
598 return;
600 // draw all glyphs
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
607 , glyphs
608 , node
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
618 , glyphs
619 , node
620 , rightCenter
621 , (state & CGraphNodeStates::COLLAPSED_RIGHT) ? ExpandGlyph : CollapseGlyph
622 , (state & CGraphNodeStates::SPLIT_RIGHT) ? JoinGlyph : SplitGlyph
623 , Right
624 , CGraphNodeStates::COLLAPSED_RIGHT
625 , CGraphNodeStates::SPLIT_RIGHT
626 , (allowed & CGraphNodeStates::COLLAPSED_RIGHT) != 0);
628 DrawGlyphs ( graphics
629 , glyphs
630 , node
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);
640 #endif
642 #if 0
643 void CRevisionGraphWnd::IndicateGlyphDirection
644 ( GraphicsDevice& graphics
645 , const ILayoutNodeList* nodeList
646 , const ILayoutNodeList::SNode& node
647 , const RectF& nodeRect
648 , DWORD glyphs
649 , bool upsideDown
650 , const CSize& offset)
652 // shortcut
654 if (glyphs == 0)
655 return;
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
666 Color 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);
678 if (indicateAbove)
680 const CVisibleGraphNode* firstAffected = node.node->GetSource();
682 assert (firstAffected);
683 RectF branchCover
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,
691 color, 1, color);
694 if (indicateRight)
696 for ( const CVisibleGraphNode::CCopyTarget* branch
697 = node.node->GetFirstCopyTarget()
698 ; branch != NULL
699 ; branch = branch->next())
701 RectF branchCover
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,
707 color, 1, color);
711 if (indicateBelow)
713 const CVisibleGraphNode* firstAffected
714 = node.node->GetNext();
716 RectF branchCover
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,
724 color, 1, color);
728 #endif
730 void CRevisionGraphWnd::DrawMarker
731 ( GraphicsDevice& graphics
732 , const RectF& noderect
733 , MarkerPosition /*position*/
734 , int /*relPosition*/
735 , const Color& penColor
736 , int num)
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);
741 if (num == 1)
743 // Roman number 1
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);
750 else if (num == 2)
752 // Roman number 2
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);
765 #if 0
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)
771 RectF clipRect;
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)
779 return;
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
791 , clipRect.Y
792 , i+1 == count ? clipRect.Width : right - left
793 , clipRect.Height);
795 // relevant?
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,
811 color, 1, color);
815 #endif
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;
829 if(dy != 0) {
830 // below
831 if(pt.Y > ymax) {
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);
838 // above
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);
849 if(dx != 0) {
850 // right
851 if(pt.X > xmax) {
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);
858 // left
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);
869 return pt;
873 void CRevisionGraphWnd::DrawConnections (GraphicsDevice& graphics, const CRect& /*logRect*/, const CSize& offset)
876 CArray<PointF> points;
877 CArray<CPoint> pts;
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
886 edge e;
887 forall_edges(e, m_Graph)
889 // get connection and point position
890 const DPolyline &dpl = this->m_GraphAttr.bends(e);
892 points.RemoveAll();
893 pts.RemoveAll();
895 PointF pt;
896 pt.X = (REAL)m_GraphAttr.x(e->source());
897 pt.Y = (REAL)m_GraphAttr.y(e->source());
899 points.Add(pt);
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;
906 points.Add(pt);
909 pt.X = (REAL)m_GraphAttr.x(e->target());
910 pt.Y = (REAL)m_GraphAttr.y(e->target());
912 points.Add(pt);
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)
920 //CPoint pt;
921 points[i].X = points[i].X * this->m_fZoomFactor - offset.cx;
922 points[i].Y = points[i].Y * this->m_fZoomFactor - offset.cy;
923 //pts.Add(pt);
926 if (graphics.graphics)
928 graphics.graphics->DrawLines(&pen, points.GetData(), (INT)points.GetCount());
931 else if (graphics.pSVG)
933 Color color;
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);
944 //draw arrow
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);
961 GraphicsPath path;
963 PointF arrows[5];
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)
997 return;
999 // iterate over all visible nodes
1001 if (graphics.pDC)
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"));
1012 node v;
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)
1027 Color background;
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,
1038 &font,
1039 Gdiplus::PointF(noderect.X + this->GetLeftRightMargin()*this->m_fZoomFactor,noderect.Y+this->GetTopBottomMargin()*m_fZoomFactor),
1040 &blackbrush);
1042 if(graphics.pSVG)
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);
1054 }else
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)
1064 CString shortname;
1065 CString str = m_HashMap[hash][i];
1066 RectF rect;
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);
1080 else
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);
1114 else
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);
1123 int mask =0;
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))),
1132 // rect);
1134 graphics.graphics->DrawString(shortname.GetBuffer(),shortname.GetLength(),
1135 &font,
1136 Gdiplus::PointF((REAL)(noderect.X + this->GetLeftRightMargin()*m_fZoomFactor),
1137 (REAL)(noderect.Y + this->GetTopBottomMargin()*m_fZoomFactor+ hight*i)),
1138 &blackbrush);
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);
1173 #if 0
1174 void CRevisionGraphWnd::DrawCurrentNodeGlyphs (GraphicsDevice& graphics, Image* glyphs, const CSize& offset)
1177 CSyncPointer<const ILayoutNodeList> nodeList (m_state.GetNodes());
1178 bool upsideDown
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)
1184 CPoint point;
1185 GetCursorPos (&point);
1186 ScreenToClient (&point);
1187 if (!GetClientRect().PtInRect (point))
1188 return;
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))
1199 : m_hoverIndex;
1201 if (nodeIndex >= nodeList->GetCount())
1202 return;
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);
1214 #endif
1216 void CRevisionGraphWnd::DrawGraph(GraphicsDevice& graphics, const CRect& rect, int nVScrollPos, int nHScrollPos, bool bDirectDraw)
1218 CMemDC* memDC = NULL;
1219 if (graphics.pDC)
1221 if (!bDirectDraw)
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
1246 if (graphics.pDC)
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);
1269 // draw preview
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);
1293 RECT tempRect;
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;
1314 delete memDC;
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"));
1321 if(mode == 0)
1323 if(this->m_HashMap.find(rev) == m_HashMap.end())
1325 CString shorthash = rev.ToString().Left(g_Git.GetShortHASHLength());
1326 RectF rect;
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(),
1332 &font,
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;
1339 else
1341 double xmax=0;
1342 double ymax=0;
1343 int lines =0;
1344 for (size_t i = 0; i < m_HashMap[rev].size(); ++i)
1346 RectF rect;
1347 CString shortref = m_HashMap[rev][i];
1348 shortref = CGit::GetShortName(shortref,NULL);
1349 if(graphics.pDC)
1351 Gdiplus::Font font(fontname.GetBuffer(), (REAL)m_nFontSize, FontStyleRegular);
1352 graphics.graphics->MeasureString(shortref.GetBuffer(), shortref.GetLength(),
1353 &font,
1354 Gdiplus::PointF(0,0), &rect);
1355 if(rect.Width > xmax)
1356 xmax = rect.Width;
1357 if(rect.Height > ymax)
1358 ymax = rect.Height;
1360 ++lines;
1362 m_GraphAttr.width(*pnode) = this->GetLeftRightMargin()*2 + xmax;
1363 m_GraphAttr.height(*pnode) = (this->GetTopBottomMargin()*2 + ymax) * lines;