1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2011 - TortoiseSVN
4 // Copyright (C) 2012-2013 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "TortoiseProc.h"
23 #include "Revisiongraphdlg.h"
24 #include "MessageBox.h"
27 #include "UnicodeUtils.h"
29 //#include "SVNInfo.h"
30 //#include ".\revisiongraphwnd.h"
31 //#include "CachedLogInfo.h"
32 //#include "RevisionGraph/IRevisionGraphLayout.h"
33 //#include "RevisionGraph/FullGraphBuilder.h"
34 //#include "RevisionGraph/FullGraphFinalizer.h"
35 //#include "RevisionGraph/VisibleGraphBuilder.h"
36 //#include "RevisionGraph/StandardLayout.h"
37 //#include "RevisionGraph/ShowWC.h"
38 //#include "RevisionGraph/ShowWCModification.h"
43 static char THIS_FILE
[] = __FILE__
;
46 using namespace Gdiplus
;
49 void CRevisionGraphWnd::InitView()
51 m_bIsCanvasMove
= false;
56 void CRevisionGraphWnd::BuildPreview()
59 m_Preview
.DeleteObject();
63 // is there a point in drawing this at all?
65 int nodeCount
= this->m_Graph
.numberOfNodes();
66 if ((nodeCount
> REVGRAPH_PREVIEW_MAX_NODES
) || (nodeCount
== 0))
69 float origZoom
= m_fZoomFactor
;
71 CRect clientRect
= GetClientRect();
72 CSize
preViewSize (max (REVGRAPH_PREVIEW_WIDTH
, clientRect
.Width() / 4)
73 ,max (REVGRAPH_PREVIEW_HEIGHT
, clientRect
.Height() / 4));
75 // zoom the graph so that it is completely visible in the window
76 CRect graphRect
= GetGraphRect();
77 float horzfact
= float(graphRect
.Width())/float(preViewSize
.cx
);
78 float vertfact
= float(graphRect
.Height())/float(preViewSize
.cy
);
79 m_previewZoom
= min (DEFAULT_ZOOM
, 1.0f
/(max(horzfact
, vertfact
)));
81 // make sure the preview window has a minimal size
83 m_previewWidth
= (int)min (max (graphRect
.Width() * m_previewZoom
, 30), preViewSize
.cx
);
84 m_previewHeight
= (int)min (max (graphRect
.Height() * m_previewZoom
, 30), preViewSize
.cy
);
88 if (!dc
.CreateCompatibleDC(&ddc
))
91 m_Preview
.CreateCompatibleBitmap(&ddc
, m_previewWidth
, m_previewHeight
);
92 HBITMAP oldbm
= (HBITMAP
)dc
.SelectObject (m_Preview
);
94 // paint the whole graph
95 DoZoom (m_previewZoom
, false);
96 CRect
rect (0, 0, m_previewWidth
, m_previewHeight
);
99 DrawGraph(dev
, rect
, 0, 0, true);
101 // now we have a bitmap the size of the preview window
102 dc
.SelectObject(oldbm
);
105 DoZoom (origZoom
, false);
109 void CRevisionGraphWnd::SetScrollbar (int bar
, int newPos
, int clientMax
, int graphMax
)
111 SCROLLINFO ScrollInfo
= {sizeof(SCROLLINFO
), SIF_ALL
};
112 GetScrollInfo (bar
, &ScrollInfo
);
114 clientMax
= max(1, clientMax
);
115 int oldHeight
= ScrollInfo
.nMax
<= 0 ? clientMax
: ScrollInfo
.nMax
;
116 int newHeight
= static_cast<int>(graphMax
* m_fZoomFactor
);
117 int maxPos
= max (0, newHeight
- clientMax
);
118 int pos
= min (maxPos
, newPos
>= 0
120 : ScrollInfo
.nPos
* newHeight
/ oldHeight
);
122 ScrollInfo
.nPos
= pos
;
124 ScrollInfo
.nMax
= newHeight
;
125 ScrollInfo
.nPage
= clientMax
;
126 ScrollInfo
.nTrackPos
= pos
;
128 SetScrollInfo(bar
, &ScrollInfo
);
132 void CRevisionGraphWnd::SetScrollbars (int nVert
, int nHorz
)
134 CRect clientrect
= GetClientRect();
135 const CRect
& pRect
= GetGraphRect();
137 SetScrollbar (SB_VERT
, nVert
, clientrect
.Height(), pRect
.Height());
138 SetScrollbar (SB_HORZ
, nHorz
, clientrect
.Width(), pRect
.Width());
141 CRect
CRevisionGraphWnd::GetGraphRect()
146 CRect
CRevisionGraphWnd::GetClientRect()
149 CWnd::GetClientRect (&clientRect
);
153 CRect
CRevisionGraphWnd::GetWindowRect()
156 CWnd::GetWindowRect (&windowRect
);
160 CRect
CRevisionGraphWnd::GetViewRect()
163 result
.UnionRect (GetClientRect(), GetGraphRect());
167 int CRevisionGraphWnd::GetEncoderClsid(const WCHAR
* format
, CLSID
* pClsid
)
169 UINT num
= 0; // number of image encoders
170 UINT size
= 0; // size of the image encoder array in bytes
172 if (GetImageEncodersSize(&num
, &size
)!=Ok
)
175 return -1; // Failure
177 ImageCodecInfo
* pImageCodecInfo
= (ImageCodecInfo
*)(malloc(size
));
178 if(pImageCodecInfo
== NULL
)
179 return -1; // Failure
181 if (GetImageEncoders(num
, size
, pImageCodecInfo
)==Ok
)
183 for(UINT j
= 0; j
< num
; ++j
)
185 if( wcscmp(pImageCodecInfo
[j
].MimeType
, format
) == 0 )
187 *pClsid
= pImageCodecInfo
[j
].Clsid
;
188 free(pImageCodecInfo
);
194 free(pImageCodecInfo
);
195 return -1; // Failure
198 bool CRevisionGraphWnd::FetchRevisionData
199 ( const CString
& /*path*/
200 , GitRev
/*pegRevision*/
201 , CProgressDlg
* /*progress*/
202 , ITaskbarList3
* /*pTaskbarList*/
205 this->m_LogCache
.ClearAllParent();
206 this->m_logEntries
.ClearAll();
208 if (!m_ToRev
.IsEmpty() && !m_FromRev
.IsEmpty())
209 range
.Format(_T("%s..%s"), g_Git
.FixBranchName(m_FromRev
), g_Git
.FixBranchName(m_ToRev
));
210 else if (!m_ToRev
.IsEmpty())
212 else if (!m_FromRev
.IsEmpty())
214 this->m_logEntries
.ParserFromLog(nullptr, -1, CGit::LOG_INFO_SIMPILFY_BY_DECORATION
|(this->m_bCurrentBranch
? 0: CGit::LOG_INFO_ALL_BRANCH
), &range
);
217 this->m_Graph
.clear();
221 dev
.pDC
= this->GetDC();
222 dev
.graphics
= Graphics::FromHDC(dev
.pDC
->m_hDC
);
223 dev
.graphics
->SetPageUnit (UnitPixel
);
225 for (int i
= 0; i
< m_logEntries
.size(); ++i
)
228 nd
= this->m_Graph
.newNode();
230 m_GraphAttr
.width(nd
)=100;
231 m_GraphAttr
.height(nd
)=20;
232 SetNodeRect(dev
, &nd
, m_logEntries
[i
], 0);
235 for (int i
= 0; i
< m_logEntries
.size(); ++i
)
237 GitRev rev
=m_logEntries
.GetGitRevAt(i
);
238 for (int j
= 0; j
< rev
.m_ParentHash
.size(); ++j
)
240 if(m_logEntries
.m_HashMap
.find(rev
.m_ParentHash
[j
]) == m_logEntries
.m_HashMap
.end())
242 TRACE(_T("Can't found parent node"));
243 //new parent node as new node
245 nd
= this->m_Graph
.newNode();
246 m_Graph
.newEdge(nodes
[i
], nd
);
247 m_logEntries
.push_back(rev
.m_ParentHash
[j
]);
248 m_logEntries
.m_HashMap
[rev
.m_ParentHash
[j
]] = (int)m_logEntries
.size() -1;
250 SetNodeRect(dev
, &nd
, rev
.m_ParentHash
[j
], 0);
254 TRACE(_T("edge %d - %d\n"),i
, m_logEntries
.m_HashMap
[rev
.m_ParentHash
[j
]]);
255 m_Graph
.newEdge(nodes
[i
], nodes
[m_logEntries
.m_HashMap
[rev
.m_ParentHash
[j
]]]);
260 //this->m_OHL.layerDistance(30.0);
261 //this->m_OHL.nodeDistance(25.0);
262 //this->m_OHL.weightBalancing(0.8);
264 m_SugiyamLayout
.call(m_GraphAttr
);
269 forall_nodes(v
,m_Graph
)
271 double x
= m_GraphAttr
.x(v
) + m_GraphAttr
.width(v
)/2;
272 double y
= m_GraphAttr
.y(v
) + m_GraphAttr
.height(v
)/2;
279 this->m_GraphRect
.top
=m_GraphRect
.left
=0;
280 m_GraphRect
.bottom
= (LONG
)ymax
;
281 m_GraphRect
.right
= (LONG
)xmax
;
286 bool CRevisionGraphWnd::AnalyzeRevisionData()
289 CSyncPointer
<const CFullGraph
> fullGraph (m_state
.GetFullGraph());
290 if ((fullGraph
.get() != NULL
) && (fullGraph
->GetNodeCount() > 0))
294 CSyncPointer
<CAllRevisionGraphOptions
> options (m_state
.GetOptions());
297 std::unique_ptr
<CVisibleGraph
> visibleGraph (new CVisibleGraph());
298 CVisibleGraphBuilder
builder ( *fullGraph
300 , options
->GetCopyFilterOptions());
302 options
->GetModificationOptions().Apply (visibleGraph
.get());
305 for (size_t i
= 0, count
= visibleGraph
->GetRootCount(); i
< count
; ++i
)
306 index
= visibleGraph
->GetRoot (i
)->InitIndex (index
);
310 std::unique_ptr
<CStandardLayout
> newLayout
311 ( new CStandardLayout ( m_state
.GetFullHistory()->GetCache()
313 , m_state
.GetFullHistory()->GetWCInfo()));
314 options
->GetLayoutOptions().Apply (newLayout
.get());
315 newLayout
->Finalize();
319 m_state
.SetAnalysisResult (visibleGraph
, newLayout
);
322 return m_state
.GetNodes().get() != NULL
;
327 bool CRevisionGraphWnd::IsUpdateJobRunning() const
329 return (updateJob
.get() != NULL
) && !updateJob
->IsDone();
332 bool CRevisionGraphWnd::GetShowOverview() const
334 return m_bShowOverview
;
337 void CRevisionGraphWnd::SetShowOverview (bool value
)
339 m_bShowOverview
= value
;
344 void CRevisionGraphWnd::GetSelected
345 ( const CVisibleGraphNode
* node
352 CString repoRoot
= m_state
.GetRepositoryRoot();
354 // get path and revision
356 path
.SetFromSVN (repoRoot
+ CUnicodeUtils::GetUnicode (node
->GetPath().GetPath().c_str()));
357 rev
= head
? GitRev::REV_HEAD
: node
->GetRevision();
359 // handle 'modified WC' node
361 if (node
->GetClassification().Is (CNodeClassification::IS_MODIFIED_WC
))
363 path
.SetFromUnknown (m_sPath
);
364 rev
= GitRev::REV_WC
;
366 // don't set peg, if we aren't the first node
367 // (i.e. would not be valid for node1)
369 if (node
== m_SelectedEntry1
)
370 peg
= GitRev::REV_WC
;
374 // set head, if still necessary
376 if (head
&& !peg
.IsValid())
377 peg
= node
->GetRevision();
383 CString
CRevisionGraphWnd::GetFriendRefName(ogdf::node v
)
387 CGitHash hash
= this->m_logEntries
[v
->index()];
388 if(this->m_HashMap
.find(hash
) == m_HashMap
.end())
389 return hash
.ToString();
390 else if(this->m_HashMap
[hash
].size() == 0)
391 return hash
.ToString();
392 else if(this->m_HashMap
[hash
][0].IsEmpty())
393 return hash
.ToString();
395 return m_HashMap
[hash
][0];
399 void CRevisionGraphWnd::CompareRevs(bool bHead
)
401 ASSERT(m_SelectedEntry1
!= NULL
);
402 ASSERT(bHead
|| m_SelectedEntry2
!= NULL
);
404 // bool alternativeTool = !!(GetAsyncKeyState(VK_SHIFT) & 0x8000);
408 sCmd
.Format(_T("/command:showcompare %s /revision1:%s /revision2:%s"),
409 this->m_sPath
.IsEmpty() ? _T("") : (_T("/path:\"") + this->m_sPath
+ _T("\"")),
410 GetFriendRefName(m_SelectedEntry1
),
411 bHead
? _T("HEAD"): GetFriendRefName(m_SelectedEntry2
));
413 CAppUtils::RunTortoiseGitProc(sCmd
);
417 void CRevisionGraphWnd::UnifiedDiffRevs(bool bHead
)
419 ASSERT(m_SelectedEntry1
!= NULL
);
420 ASSERT(bHead
|| m_SelectedEntry2
!= NULL
);
422 bool alternativeTool
= !!(GetAsyncKeyState(VK_SHIFT
) & 0x8000);
423 CAppUtils::StartShowUnifiedDiff(m_hWnd
, CString(), GetFriendRefName(m_SelectedEntry1
), CString(),
424 bHead
? _T("HEAD"):GetFriendRefName(m_SelectedEntry2
),
429 void CRevisionGraphWnd::DoZoom (float fZoomFactor
, bool updateScrollbars
)
431 float oldzoom
= m_fZoomFactor
;
432 m_fZoomFactor
= fZoomFactor
;
434 m_nFontSize
= max(1, int(DEFAULT_ZOOM_FONT
* fZoomFactor
));
435 if (m_nFontSize
< SMALL_ZOOM_FONT_THRESHOLD
)
436 m_nFontSize
= min (SMALL_ZOOM_FONT_THRESHOLD
, int(SMALL_ZOOM_FONT
* fZoomFactor
));
438 for (int i
= 0; i
< MAXFONTS
; ++i
)
440 if (m_apFonts
[i
] != NULL
)
442 m_apFonts
[i
]->DeleteObject();
448 if (updateScrollbars
)
450 SCROLLINFO si1
= {sizeof(SCROLLINFO
), SIF_ALL
};
451 GetScrollInfo(SB_VERT
, &si1
);
452 SCROLLINFO si2
= {sizeof(SCROLLINFO
), SIF_ALL
};
453 GetScrollInfo(SB_HORZ
, &si2
);
457 si1
.nPos
= int(float(si1
.nPos
)*m_fZoomFactor
/oldzoom
);
458 si2
.nPos
= int(float(si2
.nPos
)*m_fZoomFactor
/oldzoom
);
459 SetScrollPos (SB_VERT
, si1
.nPos
);
460 SetScrollPos (SB_HORZ
, si2
.nPos
);