Don't import ogdf namespace
[TortoiseGit.git] / src / TortoiseProc / RevisionGraph / RevisionGraphDlgDraw.cpp
blob8f868e074cfedb4aeb226b8212f76b820b6b4c27
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2011, 2015 - TortoiseSVN
4 // Copyright (C) 2012-2013, 2015-2018 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "TortoiseProc.h"
22 #include "MyMemDC.h"
23 #include "RevisionGraphDlg.h"
24 #include "Git.h"
25 #include "TempFile.h"
26 #include "UnicodeUtils.h"
27 #include "TGitPath.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"
34 #include "registry.h"
35 #include "UnicodeUtils.h"
36 #include "DPIAware.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;
46 Color GetColorFromSysColor(int nIndex)
48 Color color;
49 color.SetFromCOLORREF(GetSysColor(nIndex));
50 return color;
53 /************************************************************************/
54 /* Graphing functions */
55 /************************************************************************/
56 CFont* CRevisionGraphWnd::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/)
58 int nIndex = 0;
59 if (bBold)
60 nIndex |= 1;
61 if (bItalic)
62 nIndex |= 2;
63 if (!m_apFonts[nIndex])
65 m_apFonts[nIndex] = new CFont;
66 m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
67 m_lfBaseFont.lfItalic = (BYTE) bItalic;
68 m_lfBaseFont.lfStrikeOut = (BYTE) FALSE;
69 m_lfBaseFont.lfHeight = -CDPIAware::Instance().PointsToPixelsY(m_nFontSize);
70 // use the empty font name, so GDI takes the first font which matches
71 // the specs. Maybe this will help render chinese/japanese chars correctly.
72 wcsncpy_s(m_lfBaseFont.lfFaceName, L"MS Shell Dlg 2", _countof(m_lfBaseFont.lfFaceName) - 1);
73 if (!m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))
75 delete m_apFonts[nIndex];
76 m_apFonts[nIndex] = nullptr;
77 return CWnd::GetFont();
80 return m_apFonts[nIndex];
83 BOOL CRevisionGraphWnd::OnEraseBkgnd(CDC* /*pDC*/)
85 return TRUE;
88 void CRevisionGraphWnd::OnPaint()
90 CPaintDC dc(this); // device context for painting
91 CRect rect = GetClientRect();
93 if (IsUpdateJobRunning())
95 CString fetch = CString(MAKEINTRESOURCE(IDS_PROC_LOADING));
96 dc.FillSolidRect(rect, ::GetSysColor(COLOR_APPWORKSPACE));
97 dc.ExtTextOut(20, 20, ETO_CLIPPED, nullptr, fetch, nullptr);
98 CWnd::OnPaint();
99 return;
101 }else if (this->m_Graph.empty())
103 CString sNoGraphText;
104 sNoGraphText.LoadString(IDS_REVGRAPH_ERR_NOGRAPH);
105 dc.FillSolidRect(rect, RGB(255,255,255));
106 dc.ExtTextOut(20, 20, ETO_CLIPPED, nullptr, sNoGraphText, nullptr);
107 return;
110 GraphicsDevice dev;
111 dev.pDC = &dc;
112 DrawGraph(dev, rect, GetScrollPos(SB_VERT), GetScrollPos(SB_HORZ), false);
115 void CRevisionGraphWnd::ClearVisibleGlyphs (const CRect& /*rect*/)
117 #if 0
118 float glyphSize = GLYPH_SIZE * m_fZoomFactor;
120 CSyncPointer<CRevisionGraphState::TVisibleGlyphs>
121 visibleGlyphs (m_state.GetVisibleGlyphs());
123 for (size_t i = visibleGlyphs->size(), count = i; i > 0; --i)
125 const PointF& leftTop = (*visibleGlyphs)[i-1].leftTop;
126 CRect glyphRect ( static_cast<int>(leftTop.X)
127 , static_cast<int>(leftTop.Y)
128 , static_cast<int>(leftTop.X + glyphSize)
129 , static_cast<int>(leftTop.Y + glyphSize));
131 if (CRect().IntersectRect (glyphRect, rect))
133 (*visibleGlyphs)[i-1] = (*visibleGlyphs)[--count];
134 visibleGlyphs->pop_back();
137 #endif
140 void CRevisionGraphWnd::CutawayPoints (const RectF& rect, float cutLen, TCutRectangle& result)
142 result[0] = PointF (rect.X, rect.Y + cutLen);
143 result[1] = PointF (rect.X + cutLen, rect.Y);
144 result[2] = PointF (rect.GetRight() - cutLen, rect.Y);
145 result[3] = PointF (rect.GetRight(), rect.Y + cutLen);
146 result[4] = PointF (rect.GetRight(), rect.GetBottom() - cutLen);
147 result[5] = PointF (rect.GetRight() - cutLen, rect.GetBottom());
148 result[6] = PointF (rect.X + cutLen, rect.GetBottom());
149 result[7] = PointF (rect.X, rect.GetBottom() - cutLen);
152 void CRevisionGraphWnd::DrawRoundedRect (GraphicsDevice& graphics, const Color& penColor, int penWidth, const Pen* pen, const Color& fillColor, const Brush* brush, const RectF& rect, int mask)
154 enum {POINT_COUNT = 8};
156 float radius = CORNER_SIZE * m_fZoomFactor;
157 PointF points[POINT_COUNT];
158 CutawayPoints (rect, radius, points);
160 if (graphics.graphics)
162 GraphicsPath path;
164 if(mask & ROUND_UP)
166 path.AddArc (points[0].X, points[1].Y, radius, radius, 180, 90);
167 path.AddArc (points[2].X, points[2].Y, radius, radius, 270, 90);
168 }else
169 path.AddLine(points[0].X, points[1].Y, points[3].X, points[2].Y);
171 if(mask & ROUND_DOWN)
173 path.AddArc (points[5].X, points[4].Y, radius, radius, 0, 90);
174 path.AddArc (points[7].X, points[7].Y, radius, radius, 90, 90);
175 }else
177 path.AddLine(points[3].X, points[3].Y, points[4].X, points[5].Y);
178 path.AddLine(points[4].X, points[5].Y, points[7].X, points[6].Y);
181 points[0].Y -= radius / 2;
182 path.AddLine (points[7], points[0]);
184 if (brush)
185 graphics.graphics->FillPath (brush, &path);
186 if (pen)
187 graphics.graphics->DrawPath (pen, &path);
189 else if (graphics.pSVG)
190 graphics.pSVG->RoundedRectangle((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height, penColor, penWidth, fillColor, (int)radius, mask);
193 void CRevisionGraphWnd::DrawOctangle (GraphicsDevice& graphics, const Color& penColor, int penWidth, const Pen* pen, const Color& fillColor, const Brush* brush, const RectF& rect)
195 enum {POINT_COUNT = 8};
197 // show left & right edges of low boxes as "<===>"
199 float minCutAway = min (CORNER_SIZE * m_fZoomFactor, rect.Height / 2);
201 // larger boxes: remove 25% of the shorter side
203 float suggestedCutAway = min (rect.Height, rect.Width) / 4;
205 // use the more visible one of the former two
207 PointF points[POINT_COUNT];
208 CutawayPoints (rect, max (minCutAway, suggestedCutAway), points);
210 // now, draw it
212 if (graphics.graphics)
214 if (brush)
215 graphics.graphics->FillPolygon (brush, points, POINT_COUNT);
216 if (pen)
217 graphics.graphics->DrawPolygon (pen, points, POINT_COUNT);
219 else if (graphics.pSVG)
221 graphics.pSVG->Polygon(points, POINT_COUNT, penColor, penWidth, fillColor);
227 void CRevisionGraphWnd::DrawShape (GraphicsDevice& graphics, const Color& penColor, int penWidth, const Pen* pen, const Color& fillColor, const Brush* brush, const RectF& rect, NodeShape shape)
229 switch( shape )
231 case TSVNRectangle:
232 if (graphics.graphics)
234 if (brush)
235 graphics.graphics->FillRectangle (brush, rect);
236 if (pen)
237 graphics.graphics->DrawRectangle (pen, rect);
239 else if (graphics.pSVG)
241 graphics.pSVG->RoundedRectangle((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height, penColor, penWidth, fillColor);
243 break;
244 case TSVNRoundRect:
245 DrawRoundedRect (graphics, penColor, penWidth, pen, fillColor, brush, rect);
246 break;
247 case TSVNOctangle:
248 DrawOctangle (graphics, penColor, penWidth, pen, fillColor, brush, rect);
249 break;
250 case TSVNEllipse:
251 if (graphics.graphics)
253 if (brush)
254 graphics.graphics->FillEllipse (brush, rect);
255 if (pen)
256 graphics.graphics->DrawEllipse(pen, rect);
258 else if (graphics.pSVG)
259 graphics.pSVG->Ellipse((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height, penColor, penWidth, fillColor);
260 break;
261 default:
262 ASSERT(FALSE); //unknown type
263 return;
268 inline BYTE LimitedScaleColor (BYTE c1, BYTE c2, float factor)
270 BYTE scaled = c2 + (BYTE)((c1-c2)*factor);
271 return c1 < c2
272 ? max (c1, scaled)
273 : min (c1, scaled);
276 Color LimitedScaleColor (const Color& c1, const Color& c2, float factor)
278 return Color ( LimitedScaleColor (c1.GetA(), c2.GetA(), factor)
279 , LimitedScaleColor (c1.GetR(), c2.GetR(), factor)
280 , LimitedScaleColor (c1.GetG(), c2.GetG(), factor)
281 , LimitedScaleColor (c1.GetB(), c2.GetB(), factor));
284 inline BYTE Darken (BYTE c)
286 return c < 0xc0
287 ? (c / 3) * 2
288 : BYTE(int(2*c) - 0x100);
291 Color Darken (const Color& c)
293 return Color ( 0xff
294 , Darken (c.GetR())
295 , Darken (c.GetG())
296 , Darken (c.GetB()));
299 BYTE MaxComponentDiff (const Color& c1, const Color& c2)
301 int rDiff = abs ((int)c1.GetR() - (int)c2.GetR());
302 int gDiff = abs ((int)c1.GetG() - (int)c2.GetG());
303 int bDiff = abs ((int)c1.GetB() - (int)c2.GetB());
305 return (BYTE) max (max (rDiff, gDiff), bDiff);
308 #if 0
309 void CRevisionGraphWnd::DrawShadow (GraphicsDevice& graphics, const RectF& rect,
310 Color shadowColor, NodeShape shape)
312 // draw the shadow
314 RectF shadow = rect;
315 shadow.Offset (2, 2);
317 Pen pen (shadowColor);
318 SolidBrush brush (shadowColor);
320 DrawShape (graphics, shadowColor, 1, &pen, shadowColor, &brush, shadow, shape);
322 #endif
324 RectF CRevisionGraphWnd::TransformRectToScreen (const CRect& rect, const CSize& offset) const
326 PointF leftTop ( rect.left * m_fZoomFactor
327 , rect.top * m_fZoomFactor);
328 return RectF ( leftTop.X - offset.cx
329 , leftTop.Y - offset.cy
330 , rect.right * m_fZoomFactor - leftTop.X - 1
331 , rect.bottom * m_fZoomFactor - leftTop.Y);
335 RectF CRevisionGraphWnd::GetNodeRect(const ogdf::node& node, const CSize& offset) const
337 // get node and position
339 CRect rect;
340 rect.left = (int) (this->m_GraphAttr.x(node) - m_GraphAttr.width(node)/2);
341 rect.top = (int) (this->m_GraphAttr.y(node) - m_GraphAttr.height(node)/2);
342 rect.bottom = (int)( rect.top+ m_GraphAttr.height(node));
343 rect.right = (int)(rect.left + m_GraphAttr.width(node));
345 RectF noderect (TransformRectToScreen (rect, offset));
347 // show two separate lines for touching nodes,
348 // unless the scale is too small
350 if (noderect.Height > 15.0f)
351 noderect.Height -= 1.0f;
353 // done
355 return noderect;
359 #if 0
360 RectF CRev
361 isionGraphWnd::GetBranchCover
362 ( const ILayoutNodeList* nodeList
363 , index_t nodeIndex
364 , bool upward
365 , const CSize& offset)
367 // construct a rect that covers the respective branch
369 CRect cover (0, 0, 0, 0);
370 while (nodeIndex != NO_INDEX)
372 ILayoutNodeList::SNode node = nodeList->GetNode (nodeIndex);
373 cover |= node.rect;
375 const CVisibleGraphNode* nextNode = upward
376 ? node.node->GetPrevious()
377 : node.node->GetNext();
379 nodeIndex = !nextNode ? NO_INDEX : nextNode->GetIndex();
382 // expand it just a little to make it look nicer
384 cover.InflateRect (10, 2, 10, 2);
386 // and now, transfrom it
388 return TransformRectToScreen (cover, offset);
390 #endif
392 #if 0
393 void CRevisionGraphWnd::DrawShadows (GraphicsDevice& graphics, const CRect& logRect, const CSize& offset)
395 // shadow color to use
397 Color background;
398 background.SetFromCOLORREF (GetSysColor(COLOR_WINDOW));
399 Color textColor;
400 textColor.SetFromCOLORREF (GetSysColor(COLOR_WINDOWTEXT));
402 Color shadowColor = LimitedScaleColor (background, ARGB (Color::Black), 0.5f);
404 // iterate over all visible nodes
406 CSyncPointer<const ILayoutNodeList> nodes (m_state.GetNodes());
407 for ( index_t index = nodes->GetFirstVisible (logRect)
408 ; index != NO_INDEX
409 ; index = nodes->GetNextVisible (index, logRect))
411 // get node and position
413 ILayoutNodeList::SNode node = nodes->GetNode (index);
414 RectF noderect (GetNodeRect (node, offset));
416 // actual drawing
418 switch (node.style)
420 case ILayoutNodeList::SNode::STYLE_DELETED:
421 case ILayoutNodeList::SNode::STYLE_RENAMED:
422 DrawShadow (graphics, noderect, shadowColor, TSVNOctangle);
423 break;
424 case ILayoutNodeList::SNode::STYLE_ADDED:
425 DrawShadow(graphics, noderect, shadowColor, TSVNRoundRect);
426 break;
427 case ILayoutNodeList::SNode::STYLE_LAST:
428 DrawShadow(graphics, noderect, shadowColor, TSVNEllipse);
429 break;
430 default:
431 DrawShadow(graphics, noderect, shadowColor, TSVNRectangle);
432 break;
436 #endif
439 void CRevisionGraphWnd::DrawSquare
440 ( GraphicsDevice& graphics
441 , const PointF& leftTop
442 , const Color& lightColor
443 , const Color& darkColor
444 , const Color& penColor)
446 float squareSize = MARKER_SIZE * m_fZoomFactor;
448 PointF leftBottom (leftTop.X, leftTop.Y + squareSize);
449 RectF square (leftTop, SizeF (squareSize, squareSize));
451 if (graphics.graphics)
453 LinearGradientBrush lgBrush (leftTop, leftBottom, lightColor, darkColor);
454 graphics.graphics->FillRectangle (&lgBrush, square);
455 if (squareSize > 4.0f)
457 Pen pen (penColor);
458 graphics.graphics->DrawRectangle (&pen, square);
461 else if (graphics.pSVG)
463 graphics.pSVG->GradientRectangle((int)square.X, (int)square.Y, (int)square.Width, (int)square.Height,
464 lightColor, darkColor, penColor);
468 #if 0
469 void CRevisionGraphWnd::DrawGlyph
470 ( GraphicsDevice& graphics
471 , Image* glyphs
472 , const PointF& leftTop
473 , GlyphType glyph
474 , GlyphPosition position)
476 // special case
478 if (glyph == NoGlyph)
479 return;
481 // bitmap source area
483 REAL x = ((REAL)position + (REAL)glyph) * GLYPH_BITMAP_SIZE;
485 // screen target area
487 float glyphSize = GLYPH_SIZE * m_fZoomFactor;
488 RectF target (leftTop, SizeF (glyphSize, glyphSize));
490 // scaled copy
492 if (graphics.graphics)
494 graphics.graphics->DrawImage ( glyphs
495 , target
496 , x, 0.0f, GLYPH_BITMAP_SIZE, GLYPH_BITMAP_SIZE
497 , UnitPixel, nullptr, nullptr, nullptr);
499 else if (graphics.pSVG)
501 // instead of inserting a bitmap, draw a
502 // round rectangle instead.
503 // Embedding images would blow up the resulting
504 // svg file a lot, and the round rectangle
505 // is enough IMHO.
506 // Note:
507 // images could be embedded like this:
508 // <image y="100" x="100" id="imgId1234" xlink:href="data:image/png;base64,...base64endodeddata..." height="16" width="16" />
510 graphics.pSVG->RoundedRectangle((int)target.X, (int)target.Y, (int)target.Width, (int)target.Height,
511 Color(0,0,0), 2, Color(255,255,255), (int)(target.Width/3.0));
514 #endif
516 #if 0
517 void CRevisionGraphWnd::DrawGlyphs
518 ( GraphicsDevice& graphics
519 , Image* glyphs
520 , const CVisibleGraphNode* node
521 , const PointF& center
522 , GlyphType glyph1
523 , GlyphType glyph2
524 , GlyphPosition position
525 , DWORD state1
526 , DWORD state2
527 , bool showAll)
529 // don't show collapse and cut glyths by default
531 if (!showAll && ((glyph1 == CollapseGlyph) || (glyph1 == SplitGlyph)))
532 glyph1 = NoGlyph;
533 if (!showAll && ((glyph2 == CollapseGlyph) || (glyph2 == SplitGlyph)))
534 glyph2 = NoGlyph;
536 // glyth2 shall be set only if 2 glyphs are in use
538 if (glyph1 == NoGlyph)
540 std::swap (glyph1, glyph2);
541 std::swap (state1, state2);
544 // anything to do?
546 if (glyph1 == NoGlyph)
547 return;
549 // 1 or 2 glyphs?
551 CSyncPointer<CRevisionGraphState::TVisibleGlyphs>
552 visibleGlyphs (m_state.GetVisibleGlyphs());
554 float squareSize = GLYPH_SIZE * m_fZoomFactor;
555 if (glyph2 == NoGlyph)
557 PointF leftTop (center.X - 0.5f * squareSize, center.Y - 0.5f * squareSize);
558 DrawGlyph (graphics, glyphs, leftTop, glyph1, position);
559 visibleGlyphs->push_back
560 (CRevisionGraphState::SVisibleGlyph (state1, leftTop, node));
562 else
564 PointF leftTop1 (center.X - squareSize, center.Y - 0.5f * squareSize);
565 DrawGlyph (graphics, glyphs, leftTop1, glyph1, position);
566 visibleGlyphs->push_back
567 (CRevisionGraphState::SVisibleGlyph (state1, leftTop1, node));
569 PointF leftTop2 (center.X, center.Y - 0.5f * squareSize);
570 DrawGlyph (graphics, glyphs, leftTop2, glyph2, position);
571 visibleGlyphs->push_back
572 (CRevisionGraphState::SVisibleGlyph (state2, leftTop2, node));
575 #endif
577 #if 0
578 void CRevisionGraphWnd::DrawGlyphs
579 ( GraphicsDevice& graphics
580 , Image* glyphs
581 , const CVisibleGraphNode* node
582 , const RectF& nodeRect
583 , DWORD state
584 , DWORD allowed
585 , bool upsideDown)
587 // shortcut
589 if ((state == 0) && (allowed == 0))
590 return;
592 // draw all glyphs
594 PointF topCenter (0.5f * nodeRect.GetLeft() + 0.5f * nodeRect.GetRight(), nodeRect.GetTop());
595 PointF rightCenter (nodeRect.GetRight(), 0.5f * nodeRect.GetTop() + 0.5f * nodeRect.GetBottom());
596 PointF bottomCenter (0.5f * nodeRect.GetLeft() + 0.5f * nodeRect.GetRight(), nodeRect.GetBottom());
598 DrawGlyphs ( graphics
599 , glyphs
600 , node
601 , upsideDown ? bottomCenter : topCenter
602 , (state & CGraphNodeStates::COLLAPSED_ABOVE) ? ExpandGlyph : CollapseGlyph
603 , (state & CGraphNodeStates::SPLIT_ABOVE) ? JoinGlyph : SplitGlyph
604 , upsideDown ? Below : Above
605 , CGraphNodeStates::COLLAPSED_ABOVE
606 , CGraphNodeStates::SPLIT_ABOVE
607 , (allowed & CGraphNodeStates::COLLAPSED_ABOVE) != 0);
609 DrawGlyphs ( graphics
610 , glyphs
611 , node
612 , rightCenter
613 , (state & CGraphNodeStates::COLLAPSED_RIGHT) ? ExpandGlyph : CollapseGlyph
614 , (state & CGraphNodeStates::SPLIT_RIGHT) ? JoinGlyph : SplitGlyph
615 , Right
616 , CGraphNodeStates::COLLAPSED_RIGHT
617 , CGraphNodeStates::SPLIT_RIGHT
618 , (allowed & CGraphNodeStates::COLLAPSED_RIGHT) != 0);
620 DrawGlyphs ( graphics
621 , glyphs
622 , node
623 , upsideDown ? topCenter : bottomCenter
624 , (state & CGraphNodeStates::COLLAPSED_BELOW) ? ExpandGlyph : CollapseGlyph
625 , (state & CGraphNodeStates::SPLIT_BELOW) ? JoinGlyph : SplitGlyph
626 , upsideDown ? Above : Below
627 , CGraphNodeStates::COLLAPSED_BELOW
628 , CGraphNodeStates::SPLIT_BELOW
629 , (allowed & CGraphNodeStates::COLLAPSED_BELOW) != 0);
631 #endif
633 #if 0
634 void CRevisionGraphWnd::IndicateGlyphDirection
635 ( GraphicsDevice& graphics
636 , const ILayoutNodeList* nodeList
637 , const ILayoutNodeList::SNode& node
638 , const RectF& nodeRect
639 , DWORD glyphs
640 , bool upsideDown
641 , const CSize& offset)
643 // shortcut
645 if (glyphs == 0)
646 return;
648 // where to place the indication?
650 bool indicateAbove = (glyphs & CGraphNodeStates::COLLAPSED_ABOVE) != 0;
651 bool indicateRight = (glyphs & CGraphNodeStates::COLLAPSED_RIGHT) != 0;
652 bool indicateBelow = (glyphs & CGraphNodeStates::COLLAPSED_BELOW) != 0;
654 // fill indication area a semi-transparent blend of red
655 // and the background color
657 Color color;
658 color.SetFromCOLORREF (GetSysColor(COLOR_WINDOW));
659 color.SetValue ((color.GetValue() & 0x807f7f7f) + 0x800000);
661 SolidBrush brush (color);
663 // draw the indication (only one condition should match)
665 RectF glyphCenter = (indicateAbove ^ upsideDown)
666 ? RectF (nodeRect.X, nodeRect.Y - 1.0f, 0.0f, 0.0f)
667 : RectF (nodeRect.X, nodeRect.GetBottom() - 1.0f, 0.0f, 0.0f);
669 if (indicateAbove)
671 const CVisibleGraphNode* firstAffected = node.node->GetSource();
673 assert (firstAffected);
674 RectF branchCover
675 = GetBranchCover (nodeList, firstAffected->GetIndex(), true, offset);
676 RectF::Union (branchCover, branchCover, glyphCenter);
678 if (graphics.graphics)
679 graphics.graphics->FillRectangle (&brush, branchCover);
680 else if (graphics.pSVG)
681 graphics.pSVG->RoundedRectangle((int)branchCover.X, (int)branchCover.Y, (int)branchCover.Width, (int)branchCover.Height,
682 color, 1, color);
685 if (indicateRight)
687 for ( const CVisibleGraphNode::CCopyTarget* branch
688 = node.node->GetFirstCopyTarget()
689 ; branch
690 ; branch = branch->next())
692 RectF branchCover
693 = GetBranchCover (nodeList, branch->value()->GetIndex(), false, offset);
694 if (graphics.graphics)
695 graphics.graphics->FillRectangle (&brush, branchCover);
696 else if (graphics.pSVG)
697 graphics.pSVG->RoundedRectangle((int)branchCover.X, (int)branchCover.Y, (int)branchCover.Width, (int)branchCover.Height,
698 color, 1, color);
702 if (indicateBelow)
704 const CVisibleGraphNode* firstAffected
705 = node.node->GetNext();
707 RectF branchCover
708 = GetBranchCover (nodeList, firstAffected->GetIndex(), false, offset);
709 RectF::Union (branchCover, branchCover, glyphCenter);
711 if (graphics.graphics)
712 graphics.graphics->FillRectangle (&brush, branchCover);
713 else if (graphics.pSVG)
714 graphics.pSVG->RoundedRectangle((int)branchCover.X, (int)branchCover.Y, (int)branchCover.Width, (int)branchCover.Height,
715 color, 1, color);
719 #endif
721 void CRevisionGraphWnd::DrawMarker
722 ( GraphicsDevice& graphics
723 , const RectF& noderect
724 , MarkerPosition /*position*/
725 , int /*relPosition*/
726 , const Color& penColor
727 , int num)
729 REAL width = 4*this->m_fZoomFactor<1? 1: 4*this->m_fZoomFactor;
730 Pen pen(penColor,width);
731 DrawRoundedRect(graphics, penColor, (int)width, &pen, Color(0,0,0), nullptr, noderect);
732 if (num == 1)
734 // Roman number 1
735 REAL x = max(1, 10 * this->m_fZoomFactor);
736 REAL y1 = max(1, 25 * this->m_fZoomFactor);
737 REAL y2 = max(1, 5 * this->m_fZoomFactor);
738 if(graphics.graphics)
740 graphics.graphics->DrawLine(&pen, noderect.X + x, noderect.Y - y1, noderect.X + x, noderect.Y - y2);
741 if (m_SelectedEntry2)
743 CString base(L'(');
744 base.AppendFormat(IDS_PROC_DIFF_BASE);
745 base += L')';
746 SolidBrush blackbrush(penColor);
747 Gdiplus::Font font(CAppUtils::GetLogFontName(), (REAL)m_nFontSize, FontStyleRegular);
748 graphics.graphics->DrawString(base, base.GetLength(), &font, Gdiplus::PointF(noderect.X + x + width, noderect.Y - y1), &blackbrush);
752 else if (num == 2)
754 // Roman number 2
755 REAL x1 = max(1, 5 * this->m_fZoomFactor);
756 REAL x2 = max(1, 15 * this->m_fZoomFactor);
757 REAL y1 = max(1, 25 * this->m_fZoomFactor);
758 REAL y2 = max(1, 5 * this->m_fZoomFactor);
759 if(graphics.graphics)
761 graphics.graphics->DrawLine(&pen, noderect.X + x1, noderect.Y - y1, noderect.X + x1, noderect.Y - y2);
762 graphics.graphics->DrawLine(&pen, noderect.X + x2, noderect.Y - y1, noderect.X + x2, noderect.Y - y2);
767 #if 0
768 void CRevisionGraphWnd::DrawStripes (GraphicsDevice& graphics, const CSize& offset)
770 // we need to fill this visible area of the the screen
771 // (even if there is graph in that part)
773 RectF clipRect;
774 if (graphics.graphics)
775 graphics.graphics->GetVisibleClipBounds (&clipRect);
777 // don't show stripes if we don't have multiple roots
779 CSyncPointer<const ILayoutRectList> trees (m_state.GetTrees());
780 if (trees->GetCount() < 2)
781 return;
783 // iterate over all trees
785 for ( index_t i = 0, count = trees->GetCount(); i < count; ++i)
787 // screen coordinates covered by the tree
789 CRect tree = trees->GetRect(i);
790 REAL left = tree.left * m_fZoomFactor;
791 REAL right = tree.right * m_fZoomFactor;
792 RectF rect ( left - offset.cx
793 , clipRect.Y
794 , i+1 == count ? clipRect.Width : right - left
795 , clipRect.Height);
797 // relevant?
799 if (rect.IntersectsWith (clipRect))
801 // draw the background stripe
803 Color color ( (i & 1) == 0
804 ? m_Colors.GetColor (CColors::gdpStripeColor1)
805 : m_Colors.GetColor (CColors::gdpStripeColor2));
806 if (graphics.graphics)
808 SolidBrush brush (color);
809 graphics.graphics->FillRectangle (&brush, rect);
811 else if (graphics.pSVG)
812 graphics.pSVG->RoundedRectangle((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height,
813 color, 1, color);
817 #endif
819 PointF CRevisionGraphWnd::cutPoint(ogdf::node v, double lw, PointF ps, PointF pt)
821 double x = m_GraphAttr.x(v);
822 double y = m_GraphAttr.y(v);
823 double xmin = x - this->m_GraphAttr.width(v)/2 - lw/2;
824 double xmax = x + this->m_GraphAttr.width(v)/2 + lw/2;
825 double ymin = y - this->m_GraphAttr.height(v)/2 - lw/2;
826 double ymax = y + this->m_GraphAttr.height(v)/2 + lw/2;;
828 double dx = pt.X - ps.X;
829 double dy = pt.Y - ps.Y;
831 if(dy != 0) {
832 // below
833 if(pt.Y > ymax) {
834 double t = (ymax-ps.Y) / dy;
835 x = ps.X + t * dx;
837 if(xmin <= x && x <= xmax)
838 return PointF((REAL)x, (REAL)ymax);
840 // above
841 } else if(pt.Y < ymin) {
842 double t = (ymin-ps.Y) / dy;
843 x = ps.X + t * dx;
845 if(xmin <= x && x <= xmax)
846 return PointF((REAL)x, (REAL)ymin);
850 if(dx != 0) {
851 // right
852 if(pt.X > xmax) {
853 double t = (xmax-ps.X) / dx;
854 y = ps.Y + t * dy;
856 if(ymin <= y && y <= ymax)
857 return PointF((REAL)xmax, (REAL)y);
859 // left
860 } else if(pt.X < xmin) {
861 double t = (xmin-ps.X) / dx;
862 y = ps.Y + t * dy;
864 if(ymin <= y && y <= ymax)
865 return PointF((REAL)xmin, (REAL)y);
869 return pt;
872 void CRevisionGraphWnd::DrawConnections (GraphicsDevice& graphics, const CRect& /*logRect*/, const CSize& offset)
874 CArray<PointF> points;
875 CArray<CPoint> pts;
877 if(graphics.graphics)
878 graphics.graphics->SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
880 float penwidth = 2*m_fZoomFactor<1? 1:2*m_fZoomFactor;
881 Gdiplus::Pen pen(GetColorFromSysColor(COLOR_WINDOWTEXT), penwidth);
883 // iterate over all visible lines
884 ogdf::edge e;
885 forall_edges(e, m_Graph)
887 // get connection and point position
888 const auto& dpl = this->m_GraphAttr.bends(e);
890 points.RemoveAll();
891 pts.RemoveAll();
893 PointF pt;
894 pt.X = (REAL)m_GraphAttr.x(e->source());
895 pt.Y = (REAL)m_GraphAttr.y(e->source());
897 points.Add(pt);
899 for (auto it = dpl.begin(); it.valid(); ++it)
901 pt.X = (REAL)(*it).m_x;
902 pt.Y = (REAL)(*it).m_y;
903 points.Add(pt);
906 pt.X = (REAL)m_GraphAttr.x(e->target());
907 pt.Y = (REAL)m_GraphAttr.y(e->target());
909 points.Add(pt);
911 points[0] = this->cutPoint(e->source(), 1, points[0], points[1]);
912 points[points.GetCount()-1] = this->cutPoint(e->target(), 1, points[points.GetCount()-1], points[points.GetCount()-2]);
913 // draw the connection
915 for (int i = 0; i < points.GetCount(); ++i)
917 //CPoint pt;
918 points[i].X = points[i].X * this->m_fZoomFactor - offset.cx;
919 points[i].Y = points[i].Y * this->m_fZoomFactor - offset.cy;
920 //pts.Add(pt);
923 if (graphics.graphics)
924 graphics.graphics->DrawLines(&pen, points.GetData(), (INT)points.GetCount());
925 else if (graphics.pSVG)
926 graphics.pSVG->Polyline(points.GetData(), (int)points.GetCount(), Color(0,0,0), (int)penwidth);
927 else if (graphics.pGraphviz)
929 CString hash1 = L'g' + m_logEntries[e->target()->index()].ToString().Left(g_Git.GetShortHASHLength());
930 CString hash2 = L'g' + m_logEntries[e->source()->index()].ToString().Left(g_Git.GetShortHASHLength());
931 graphics.pGraphviz->DrawEdge(hash1, hash2);
934 //draw arrow
935 double dx = points[1].X - points[0].X;
936 double dy = points[1].Y - points[0].Y;
938 double len = sqrt(dx*dx + dy*dy);
939 dx = m_ArrowSize * m_fZoomFactor *dx /len;
940 dy = m_ArrowSize * m_fZoomFactor *dy /len;
942 double p1_x, p1_y, p2_x, p2_y;
943 p1_x = dx * m_ArrowCos - dy * m_ArrowSin;
944 p1_y = dx * m_ArrowSin + dy * m_ArrowCos;
946 p2_x = dx * m_ArrowCos + dy * m_ArrowSin;
947 p2_y = -dx * m_ArrowSin + dy * m_ArrowCos;
949 //graphics.graphics->DrawLine(&pen, points[0].X,points[0].Y, points[0].X +p1_x,points[0].Y+p1_y);
950 //graphics.graphics->DrawLine(&pen, points[0].X,points[0].Y, points[0].X +p2_x,points[0].Y+p2_y);
951 GraphicsPath path;
953 PointF arrows[5];
954 arrows[0].X = points[0].X;
955 arrows[0].Y = points[0].Y;
957 arrows[1].X = points[0].X + (REAL)p1_x;
958 arrows[1].Y = points[0].Y + (REAL)p1_y;
960 arrows[2].X = points[0].X + (REAL)dx*3/5;
961 arrows[2].Y = points[0].Y + (REAL)dy*3/5;
963 arrows[3].X = points[0].X + (REAL)p2_x;
964 arrows[3].Y = points[0].Y + (REAL)p2_y;
966 arrows[4].X = points[0].X;
967 arrows[4].Y = points[0].Y;
969 path.AddLines(arrows, 5);
970 path.SetFillMode(FillModeAlternate);
971 if(graphics.graphics)
972 graphics.graphics->DrawPath(&pen, &path);
973 else if(graphics.pSVG)
974 graphics.pSVG->DrawPath(arrows, 5, Color(0,0,0), (int)penwidth, Color(0,0,0));
978 void CRevisionGraphWnd::DrawTexts (GraphicsDevice& graphics, const CRect& /*logRect*/, const CSize& offset)
980 //COLORREF standardTextColor = GetSysColor(COLOR_WINDOWTEXT);
981 if (m_nFontSize <= 0)
982 return;
984 // iterate over all visible nodes
986 if (graphics.pDC)
987 graphics.pDC->SetTextAlign (TA_CENTER | TA_TOP);
989 CString fontname = CAppUtils::GetLogFontName();
991 Gdiplus::Font font(fontname, (REAL)m_nFontSize, FontStyleRegular);
992 SolidBrush blackbrush((ARGB)Color::Black);
994 DWORD revGraphUseLocalForCur = CRegDWORD(L"Software\\TortoiseGit\\TortoiseProc\\Graph\\RevGraphUseLocalForCur");
996 ogdf::node v;
997 forall_nodes(v,m_Graph)
999 // get node and position
1000 RectF noderect (GetNodeRect (v, offset));
1002 // draw the revision text
1003 CGitHash hash = this->m_logEntries[v->index()];
1004 double hight = noderect.Height / (!m_HashMap[hash].empty() ? m_HashMap[hash].size() : 1);
1006 if (m_HashMap.find(hash) == m_HashMap.end() || m_HashMap[hash].empty())
1008 Color background;
1009 background.SetFromCOLORREF (GetSysColor(COLOR_WINDOW));
1010 Gdiplus::Pen pen(background,1.0F);
1011 Color brightColor = LimitedScaleColor (background, RGB(255,0,0), 0.9f);
1012 Gdiplus::SolidBrush brush(brightColor);
1014 DrawRoundedRect(graphics, background,1, &pen, brightColor, &brush, noderect);
1016 if(graphics.graphics)
1018 graphics.graphics->DrawString(hash.ToString().Left(g_Git.GetShortHASHLength()),-1,
1019 &font,
1020 Gdiplus::PointF(noderect.X + this->GetLeftRightMargin()*this->m_fZoomFactor,noderect.Y+this->GetTopBottomMargin()*m_fZoomFactor),
1021 &blackbrush);
1023 if(graphics.pSVG)
1025 graphics.pSVG->Text((int)(noderect.X + this->GetLeftRightMargin() * this->m_fZoomFactor),
1026 (int)(noderect.Y + this->GetTopBottomMargin() * m_fZoomFactor + m_nFontSize),
1027 CUnicodeUtils::GetUTF8(fontname), m_nFontSize, false, false, (ARGB)Color::Black,
1028 CUnicodeUtils::GetUTF8(hash.ToString().Left(g_Git.GetShortHASHLength())));
1030 if (graphics.pGraphviz)
1032 CString shortHash = hash.ToString().Left(g_Git.GetShortHASHLength());
1033 graphics.pGraphviz->DrawNode(L'g' + shortHash, shortHash, fontname, m_nFontSize, background, brightColor, (int)noderect.Height);
1035 }else
1037 if (graphics.pGraphviz)
1039 CString id = L'g' + hash.ToString().Left(g_Git.GetShortHASHLength());
1040 graphics.pGraphviz->BeginDrawTableNode(id, fontname, m_nFontSize, (int)noderect.Height);
1043 for (size_t i = 0; i < m_HashMap[hash].size(); ++i)
1045 CString shortname;
1046 CString str = m_HashMap[hash][i];
1047 RectF rect;
1049 rect.X = (REAL)noderect.X;
1050 rect.Y = (REAL)(noderect.Y + hight*i);
1051 rect.Width = (REAL)noderect.Width;
1052 rect.Height = (REAL)hight;
1054 COLORREF colRef = m_Colors.GetColor(CColors::OtherRef);
1056 CGit::REF_TYPE refType;
1057 shortname = CGit::GetShortName(str, &refType);
1058 switch (refType)
1060 case CGit::REF_TYPE::LOCAL_BRANCH:
1061 if (!revGraphUseLocalForCur && shortname == m_CurrentBranch)
1062 colRef = m_Colors.GetColor(CColors::CurrentBranch);
1063 else
1064 colRef = m_Colors.GetColor(CColors::LocalBranch);
1065 break;
1066 case CGit::REF_TYPE::REMOTE_BRANCH:
1067 colRef = m_Colors.GetColor(CColors::RemoteBranch);
1068 break;
1069 case CGit::REF_TYPE::ANNOTATED_TAG:
1070 case CGit::REF_TYPE::TAG:
1071 colRef = m_Colors.GetColor(CColors::Tag);
1072 break;
1073 case CGit::REF_TYPE::STASH:
1074 colRef = m_Colors.GetColor(CColors::Stash);
1075 break;
1076 case CGit::REF_TYPE::BISECT_GOOD:
1077 colRef = m_Colors.GetColor(CColors::BisectGood);
1078 break;
1079 case CGit::REF_TYPE::BISECT_BAD:
1080 colRef = m_Colors.GetColor(CColors::BisectBad);
1081 break;
1082 case CGit::REF_TYPE::BISECT_SKIP:
1083 colRef = m_Colors.GetColor(CColors::BisectSkip);
1084 break;
1085 case CGit::REF_TYPE::NOTES:
1086 colRef = m_Colors.GetColor(CColors::NoteNode);
1087 break;
1090 Gdiplus::Color color(GetRValue(colRef), GetGValue(colRef), GetBValue(colRef));
1091 Gdiplus::Pen pen(color);
1092 Gdiplus::SolidBrush brush(color);
1094 int mask =0;
1095 mask |= (i==0)? ROUND_UP:0;
1096 mask |= (i== m_HashMap[hash].size()-1)? ROUND_DOWN:0;
1097 this->DrawRoundedRect(graphics, color,1,&pen, color,&brush, rect,mask);
1099 if (graphics.graphics)
1101 //graphics.graphics->FillRectangle(&SolidBrush(Gdiplus::Color(GetRValue(colRef), GetGValue(colRef), GetBValue(colRef))),
1102 // rect);
1104 graphics.graphics->DrawString(shortname, shortname.GetLength(),
1105 &font,
1106 Gdiplus::PointF((REAL)(noderect.X + this->GetLeftRightMargin()*m_fZoomFactor),
1107 (REAL)(noderect.Y + this->GetTopBottomMargin()*m_fZoomFactor+ hight*i)),
1108 &blackbrush);
1110 //graphics.graphics->DrawString(shortname.GetBuffer(), shortname.GetLength(), ::new Gdiplus::Font(graphics.pDC->m_hDC), PointF(noderect.X, noderect.Y + hight * i), nullptr, nullptr);
1113 else if (graphics.pSVG)
1114 graphics.pSVG->Text((int)(noderect.X + this->GetLeftRightMargin() * m_fZoomFactor),
1115 (int)(noderect.Y + this->GetTopBottomMargin() * m_fZoomFactor + hight * i + m_nFontSize),
1116 CUnicodeUtils::GetUTF8(fontname), m_nFontSize,
1117 false, false, (ARGB)Color::Black, CUnicodeUtils::GetUTF8(shortname));
1118 else if (graphics.pGraphviz)
1119 graphics.pGraphviz->DrawTableNode(shortname, color);
1122 if (graphics.pGraphviz)
1123 graphics.pGraphviz->EndDrawTableNode();
1125 if ((m_SelectedEntry1 == v))
1126 DrawMarker(graphics, noderect, mpLeft, 0, GetColorFromSysColor(COLOR_HIGHLIGHT), 1);
1128 if ((m_SelectedEntry2 == v))
1129 DrawMarker(graphics, noderect, mpLeft, 0, Color(136,0, 21), 2);
1134 #if 0
1135 void CRevisionGraphWnd::DrawCurrentNodeGlyphs (GraphicsDevice& graphics, Image* glyphs, const CSize& offset)
1137 CSyncPointer<const ILayoutNodeList> nodeList (m_state.GetNodes());
1138 bool upsideDown
1139 = m_state.GetOptions()->GetOption<CUpsideDownLayout>()->IsActive();
1141 // don't draw glyphs if we are outside the client area
1142 // (e.g. within a scrollbar)
1144 CPoint point;
1145 GetCursorPos (&point);
1146 ScreenToClient (&point);
1147 if (!GetClientRect().PtInRect (point))
1148 return;
1150 // expansion glypths etc.
1152 m_hoverIndex = GetHitNode (point);
1153 m_hoverGlyphs = GetHoverGlyphs (point);
1155 if ((m_hoverIndex != NO_INDEX) || (m_hoverGlyphs != 0))
1157 index_t nodeIndex = m_hoverIndex == NO_INDEX
1158 ? GetHitNode (point, CSize (GLYPH_SIZE, GLYPH_SIZE / 2))
1159 : m_hoverIndex;
1161 if (nodeIndex >= nodeList->GetCount())
1162 return;
1164 ILayoutNodeList::SNode node = nodeList->GetNode (nodeIndex);
1165 RectF noderect (GetNodeRect (node, offset));
1167 DWORD flags = m_state.GetNodeStates()->GetFlags (node.node);
1169 IndicateGlyphDirection (graphics, nodeList.get(), node, noderect, m_hoverGlyphs, upsideDown, offset);
1170 DrawGlyphs (graphics, glyphs, node.node, noderect, flags, m_hoverGlyphs, upsideDown);
1173 #endif
1175 void CRevisionGraphWnd::DrawGraph(GraphicsDevice& graphics, const CRect& rect, int nVScrollPos, int nHScrollPos, bool bDirectDraw)
1177 CMemDC* memDC = nullptr;
1178 if (graphics.pDC)
1180 if (!bDirectDraw)
1182 memDC = new CMemDC (*graphics.pDC, rect);
1183 graphics.pDC = &memDC->GetDC();
1186 graphics.pDC->FillSolidRect(rect, GetSysColor(COLOR_WINDOW));
1187 graphics.pDC->SetBkMode(TRANSPARENT);
1190 // preparation & sync
1192 //CSyncPointer<CAllRevisionGraphOptions> options (m_state.GetOptions());
1193 ClearVisibleGlyphs (rect);
1195 // transform visible
1197 CSize offset (nHScrollPos, nVScrollPos);
1198 CRect logRect ( (int)(offset.cx / m_fZoomFactor)-1
1199 , (int)(offset.cy / m_fZoomFactor)-1
1200 , (int)((rect.Width() + offset.cx) / m_fZoomFactor) + 1
1201 , (int)((rect.Height() + offset.cy) / m_fZoomFactor) + 1);
1203 // draw the different components
1205 if (graphics.pDC)
1207 Graphics* gcs = Graphics::FromHDC(*graphics.pDC);
1208 graphics.graphics = gcs;
1209 gcs->SetPageUnit (UnitPixel);
1210 gcs->SetInterpolationMode (InterpolationModeHighQualityBicubic);
1211 gcs->SetSmoothingMode(SmoothingModeAntiAlias);
1212 gcs->SetClip(RectF(Gdiplus::REAL(rect.left), Gdiplus::REAL(rect.top), Gdiplus::REAL(rect.Width()), Gdiplus::REAL(rect.Height())));
1215 // if (options->GetOption<CShowTreeStripes>()->IsActive())
1216 // DrawStripes (graphics, offset);
1218 //if (m_fZoomFactor > SHADOW_ZOOM_THRESHOLD)
1219 // DrawShadows (graphics, logRect, offset);
1221 Bitmap glyphs (AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_REVGRAPHGLYPHS));
1223 DrawTexts (graphics, logRect, offset);
1224 DrawConnections (graphics, logRect, offset);
1225 //if (m_showHoverGlyphs)
1226 // DrawCurrentNodeGlyphs (graphics, &glyphs, offset);
1228 // draw preview
1230 if ((!bDirectDraw)&&(m_Preview.GetSafeHandle())&&(m_bShowOverview)&&(graphics.pDC))
1232 // draw the overview image rectangle in the top right corner
1233 CMyMemDC memDC2(graphics.pDC, true);
1234 memDC2.SetWindowOrg(0, 0);
1235 HBITMAP oldhbm = (HBITMAP)memDC2.SelectObject(&m_Preview);
1236 graphics.pDC->BitBlt(rect.Width()-m_previewWidth, rect.Height() - m_previewHeight, m_previewWidth, m_previewHeight,
1237 &memDC2, 0, 0, SRCCOPY);
1238 memDC2.SelectObject(oldhbm);
1239 // draw the border for the overview rectangle
1240 m_OverviewRect.left = rect.Width()-m_previewWidth;
1241 m_OverviewRect.top = rect.Height()- m_previewHeight;
1242 m_OverviewRect.right = rect.Width();
1243 m_OverviewRect.bottom = rect.Height();
1244 graphics.pDC->DrawEdge(&m_OverviewRect, EDGE_BUMP, BF_RECT);
1245 // now draw a rectangle where the current view is located in the overview
1247 LONG width = (long)(rect.Width() * m_previewZoom / m_fZoomFactor);
1248 LONG height = (long)(rect.Height() * m_previewZoom / m_fZoomFactor);
1249 LONG xpos = (long)(nHScrollPos * m_previewZoom / m_fZoomFactor);
1250 LONG ypos = (long)(nVScrollPos * m_previewZoom / m_fZoomFactor);
1251 RECT tempRect;
1252 tempRect.left = rect.Width()-m_previewWidth+xpos;
1253 tempRect.top = rect.Height() - m_previewHeight + ypos;
1254 tempRect.right = tempRect.left + width;
1255 tempRect.bottom = tempRect.top + height;
1256 // make sure the position rect is not bigger than the preview window itself
1257 ::IntersectRect(&m_OverviewPosRect, &m_OverviewRect, &tempRect);
1259 RectF rect2 ( (float)m_OverviewPosRect.left, (float)m_OverviewPosRect.top
1260 , (float)m_OverviewPosRect.Width(), (float)m_OverviewPosRect.Height());
1261 if (graphics.graphics)
1263 SolidBrush brush (Color (64, 0, 0, 0));
1264 graphics.graphics->FillRectangle (&brush, rect2);
1265 graphics.pDC->DrawEdge(&m_OverviewPosRect, EDGE_BUMP, BF_RECT);
1269 // flush changes to screen
1271 delete graphics.graphics;
1272 delete memDC;
1275 void CRevisionGraphWnd::SetNodeRect(GraphicsDevice& graphics, ogdf::node *pnode, CGitHash rev, int mode )
1277 //multi - line mode. One RefName is one new line
1278 CString fontname = CAppUtils::GetLogFontName();
1279 if(mode == 0)
1281 if(this->m_HashMap.find(rev) == m_HashMap.end())
1283 CString shorthash = rev.ToString().Left(g_Git.GetShortHASHLength());
1284 RectF rect;
1285 if(graphics.graphics)
1287 //GetTextExtentPoint32(graphics.pDC->m_hDC, shorthash.GetBuffer(), shorthash.GetLength(), &size);
1288 Gdiplus::Font font(fontname, (REAL)m_nFontSize, FontStyleRegular);
1289 graphics.graphics->MeasureString(shorthash, shorthash.GetLength(),
1290 &font,
1291 Gdiplus::PointF(0,0), &rect);
1293 m_GraphAttr.width(*pnode) = this->GetLeftRightMargin()*2 + rect.Width;
1294 m_GraphAttr.height(*pnode) = this->GetTopBottomMargin()*2 + rect.Height;
1296 else
1298 double xmax=0;
1299 double ymax=0;
1300 int lines =0;
1301 for (size_t i = 0; i < m_HashMap[rev].size(); ++i)
1303 RectF rect;
1304 CString shortref = m_HashMap[rev][i];
1305 shortref = CGit::GetShortName(shortref, nullptr);
1306 if(graphics.pDC)
1308 Gdiplus::Font font(fontname, (REAL)m_nFontSize, FontStyleRegular);
1309 graphics.graphics->MeasureString(shortref, shortref.GetLength(),
1310 &font,
1311 Gdiplus::PointF(0,0), &rect);
1312 if(rect.Width > xmax)
1313 xmax = rect.Width;
1314 if(rect.Height > ymax)
1315 ymax = rect.Height;
1317 ++lines;
1319 m_GraphAttr.width(*pnode) = this->GetLeftRightMargin()*2 + xmax;
1320 m_GraphAttr.height(*pnode) = (this->GetTopBottomMargin()*2 + ymax) * lines;