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