Fixed issue #2180: When opening revision graph, scroll to current branch
[TortoiseGit.git] / src / TortoiseProc / RevisionGraph / RevisionGraphDlgFunc.cpp
blob8172ee022963a113182d6a2abef3329ab8dcf2f5
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2011 - TortoiseSVN
4 // Copyright (C) 2012-2014 - 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 <gdiplus.h>
23 #include "Revisiongraphdlg.h"
24 #include "MessageBox.h"
25 #include "Git.h"
26 #include "TempFile.h"
27 #include "UnicodeUtils.h"
28 #include "TGitPath.h"
29 //#include "SVNInfo.h"
30 //#include ".\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"
40 #ifdef _DEBUG
41 #define new DEBUG_NEW
42 #undef THIS_FILE
43 static char THIS_FILE[] = __FILE__;
44 #endif
46 using namespace Gdiplus;
47 using namespace ogdf;
49 void CRevisionGraphWnd::InitView()
51 m_bIsCanvasMove = false;
53 SetScrollbars();
56 void CRevisionGraphWnd::BuildPreview()
59 m_Preview.DeleteObject();
60 if (!m_bShowOverview)
61 return;
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))
67 return;
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);
86 CClientDC ddc(this);
87 CDC dc;
88 if (!dc.CreateCompatibleDC(&ddc))
89 return;
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);
97 GraphicsDevice dev;
98 dev.pDC = &dc;
99 DrawGraph(dev, rect, 0, 0, true);
101 // now we have a bitmap the size of the preview window
102 dc.SelectObject(oldbm);
103 dc.DeleteDC();
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
119 ? newPos
120 : ScrollInfo.nPos * newHeight / oldHeight);
122 ScrollInfo.nPos = pos;
123 ScrollInfo.nMin = 0;
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()
143 return m_GraphRect;
146 CRect CRevisionGraphWnd::GetClientRect()
148 CRect clientRect;
149 CWnd::GetClientRect (&clientRect);
150 return clientRect;
153 CRect CRevisionGraphWnd::GetWindowRect()
155 CRect windowRect;
156 CWnd::GetWindowRect (&windowRect);
157 return windowRect;
160 CRect CRevisionGraphWnd::GetViewRect()
162 CRect result;
163 result.UnionRect (GetClientRect(), GetGraphRect());
164 return result;
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)
173 return -1;
174 if(size == 0)
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);
189 return j; // Success
194 free(pImageCodecInfo);
195 return -1; // Failure
198 bool CRevisionGraphWnd::FetchRevisionData
199 ( const CString& /*path*/
200 , GitRev /*pegRevision*/
201 , CProgressDlg* /*progress*/
202 , ITaskbarList3 * /*pTaskbarList*/
203 , HWND /*hWnd*/)
205 this->m_LogCache.ClearAllParent();
206 this->m_logEntries.ClearAll();
207 CString range;
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())
211 range = m_ToRev;
212 else if (!m_FromRev.IsEmpty())
213 range = m_FromRev;
214 this->m_logEntries.ParserFromLog(nullptr, -1, CGit::LOG_INFO_SIMPILFY_BY_DECORATION|(this->m_bCurrentBranch? 0: CGit::LOG_INFO_ALL_BRANCH), &range);
216 ReloadHashMap();
217 this->m_Graph.clear();
219 CArray<node> nodes;
220 GraphicsDevice dev;
221 dev.pDC = this->GetDC();
222 dev.graphics = Graphics::FromHDC(dev.pDC->m_hDC);
223 dev.graphics->SetPageUnit (UnitPixel);
225 CGitHash head;
226 g_Git.GetHash(head, _T("HEAD"));
227 m_HeadNode = nullptr;
229 for (size_t i = 0; i < m_logEntries.size(); ++i)
231 node nd;
232 nd = this->m_Graph.newNode();
233 nodes.Add(nd);
234 m_GraphAttr.width(nd)=100;
235 m_GraphAttr.height(nd)=20;
236 SetNodeRect(dev, &nd, m_logEntries[i], 0);
237 if (m_logEntries[i] == head)
238 m_HeadNode = nd;
241 for (size_t i = 0; i < m_logEntries.size(); ++i)
243 GitRev rev=m_logEntries.GetGitRevAt(i);
244 for (size_t j = 0; j < rev.m_ParentHash.size(); ++j)
246 if(m_logEntries.m_HashMap.find(rev.m_ParentHash[j]) == m_logEntries.m_HashMap.end())
248 TRACE(_T("Can't found parent node"));
249 //new parent node as new node
250 node nd;
251 nd = this->m_Graph.newNode();
252 m_Graph.newEdge(nodes[i], nd);
253 m_logEntries.push_back(rev.m_ParentHash[j]);
254 m_logEntries.m_HashMap[rev.m_ParentHash[j]] = (int)m_logEntries.size() -1;
255 nodes.Add(nd);
256 SetNodeRect(dev, &nd, rev.m_ParentHash[j], 0);
258 }else
260 TRACE(_T("edge %d - %d\n"),i, m_logEntries.m_HashMap[rev.m_ParentHash[j]]);
261 m_Graph.newEdge(nodes[i], nodes[m_logEntries.m_HashMap[rev.m_ParentHash[j]]]);
266 //this->m_OHL.layerDistance(30.0);
267 //this->m_OHL.nodeDistance(25.0);
268 //this->m_OHL.weightBalancing(0.8);
270 m_SugiyamLayout.call(m_GraphAttr);
272 node v;
273 double xmax = 0;
274 double ymax = 0;
275 forall_nodes(v,m_Graph)
277 double x = m_GraphAttr.x(v) + m_GraphAttr.width(v)/2;
278 double y = m_GraphAttr.y(v) + m_GraphAttr.height(v)/2;
279 if(x>xmax)
280 xmax = x;
281 if(y>ymax)
282 ymax = y;
285 this->m_GraphRect.top=m_GraphRect.left=0;
286 m_GraphRect.bottom = (LONG)ymax;
287 m_GraphRect.right = (LONG)xmax;
289 return true;
292 bool CRevisionGraphWnd::AnalyzeRevisionData()
294 #if 0
295 CSyncPointer<const CFullGraph> fullGraph (m_state.GetFullGraph());
296 if ((fullGraph.get() != NULL) && (fullGraph->GetNodeCount() > 0))
298 // filter graph
300 CSyncPointer<CAllRevisionGraphOptions> options (m_state.GetOptions());
301 options->Prepare();
303 std::unique_ptr<CVisibleGraph> visibleGraph (new CVisibleGraph());
304 CVisibleGraphBuilder builder ( *fullGraph
305 , *visibleGraph
306 , options->GetCopyFilterOptions());
307 builder.Run();
308 options->GetModificationOptions().Apply (visibleGraph.get());
310 index_t index = 0;
311 for (size_t i = 0, count = visibleGraph->GetRootCount(); i < count; ++i)
312 index = visibleGraph->GetRoot (i)->InitIndex (index);
314 // layout nodes
316 std::unique_ptr<CStandardLayout> newLayout
317 ( new CStandardLayout ( m_state.GetFullHistory()->GetCache()
318 , visibleGraph.get()
319 , m_state.GetFullHistory()->GetWCInfo()));
320 options->GetLayoutOptions().Apply (newLayout.get());
321 newLayout->Finalize();
323 // switch state
325 m_state.SetAnalysisResult (visibleGraph, newLayout);
328 return m_state.GetNodes().get() != NULL;
329 #endif
330 return true;
333 bool CRevisionGraphWnd::IsUpdateJobRunning() const
335 return (updateJob.get() != NULL) && !updateJob->IsDone();
338 bool CRevisionGraphWnd::GetShowOverview() const
340 return m_bShowOverview;
343 void CRevisionGraphWnd::SetShowOverview (bool value)
345 m_bShowOverview = value;
346 if (m_bShowOverview)
347 BuildPreview();
349 #if 0
350 void CRevisionGraphWnd::GetSelected
351 ( const CVisibleGraphNode* node
352 , bool head
353 , CTGitPath& path
354 , GitRev& rev
355 , GitRev& peg)
358 CString repoRoot = m_state.GetRepositoryRoot();
360 // get path and revision
362 path.SetFromSVN (repoRoot + CUnicodeUtils::GetUnicode (node->GetPath().GetPath().c_str()));
363 rev = head ? GitRev::REV_HEAD : node->GetRevision();
365 // handle 'modified WC' node
367 if (node->GetClassification().Is (CNodeClassification::IS_MODIFIED_WC))
369 path.SetFromUnknown (m_sPath);
370 rev = GitRev::REV_WC;
372 // don't set peg, if we aren't the first node
373 // (i.e. would not be valid for node1)
375 if (node == m_SelectedEntry1)
376 peg = GitRev::REV_WC;
378 else
380 // set head, if still necessary
382 if (head && !peg.IsValid())
383 peg = node->GetRevision();
387 #endif
389 CString CRevisionGraphWnd::GetFriendRefName(ogdf::node v)
391 if(v == NULL)
392 return CString();
393 CGitHash hash = this->m_logEntries[v->index()];
394 if(this->m_HashMap.find(hash) == m_HashMap.end())
395 return hash.ToString();
396 else if (m_HashMap[hash].empty())
397 return hash.ToString();
398 else if(this->m_HashMap[hash][0].IsEmpty())
399 return hash.ToString();
400 else
401 return m_HashMap[hash][0];
405 STRING_VECTOR CRevisionGraphWnd::GetFriendRefNames(ogdf::node v, CGit::REF_TYPE *refTypes, int refTypeCount)
407 if (v == NULL)
408 return STRING_VECTOR();
409 CGitHash hash = m_logEntries[v->index()];
410 if (m_HashMap.find(hash) == m_HashMap.end())
411 return STRING_VECTOR();
412 else if (m_HashMap[hash].empty())
413 return STRING_VECTOR();
414 else if (m_HashMap[hash][0].IsEmpty())
415 return STRING_VECTOR();
416 else
418 STRING_VECTOR &all = m_HashMap[hash];
419 STRING_VECTOR list;
420 for (size_t i = 0; i < all.size(); ++i)
422 CGit::REF_TYPE refType;
423 CString shortName = CGit::GetShortName(all[i], &refType);
424 if (refTypes == NULL)
425 list.push_back(shortName);
426 else
428 for (int i = 0; i < refTypeCount; ++i)
429 if (refTypes[i] == refType)
430 list.push_back(shortName);
433 return list;
437 void CRevisionGraphWnd::CompareRevs(const CString& revTo)
439 ASSERT(m_SelectedEntry1 != NULL);
440 ASSERT(!revTo.IsEmpty() || m_SelectedEntry2 != NULL);
442 // bool alternativeTool = !!(GetAsyncKeyState(VK_SHIFT) & 0x8000);
444 CString sCmd;
446 sCmd.Format(_T("/command:showcompare %s /revision1:%s /revision2:%s"),
447 this->m_sPath.IsEmpty() ? _T("") : (_T("/path:\"") + this->m_sPath + _T("\"")),
448 GetFriendRefName(m_SelectedEntry1),
449 !revTo.IsEmpty() ? revTo : GetFriendRefName(m_SelectedEntry2));
451 CAppUtils::RunTortoiseGitProc(sCmd);
455 void CRevisionGraphWnd::UnifiedDiffRevs(bool bHead)
457 ASSERT(m_SelectedEntry1 != NULL);
458 ASSERT(bHead || m_SelectedEntry2 != NULL);
460 bool alternativeTool = !!(GetAsyncKeyState(VK_SHIFT) & 0x8000);
461 CAppUtils::StartShowUnifiedDiff(m_hWnd, CString(), GetFriendRefName(m_SelectedEntry1), CString(),
462 bHead? _T("HEAD"):GetFriendRefName(m_SelectedEntry2),
463 alternativeTool);
467 void CRevisionGraphWnd::DoZoom (float fZoomFactor, bool updateScrollbars)
469 float oldzoom = m_fZoomFactor;
470 m_fZoomFactor = fZoomFactor;
472 m_nFontSize = max(1, int(DEFAULT_ZOOM_FONT * fZoomFactor));
473 if (m_nFontSize < SMALL_ZOOM_FONT_THRESHOLD)
474 m_nFontSize = min (SMALL_ZOOM_FONT_THRESHOLD, int(SMALL_ZOOM_FONT * fZoomFactor));
476 for (int i = 0; i < MAXFONTS; ++i)
478 if (m_apFonts[i] != NULL)
480 m_apFonts[i]->DeleteObject();
481 delete m_apFonts[i];
483 m_apFonts[i] = NULL;
486 if (updateScrollbars)
488 SCROLLINFO si1 = {sizeof(SCROLLINFO), SIF_ALL};
489 GetScrollInfo(SB_VERT, &si1);
490 SCROLLINFO si2 = {sizeof(SCROLLINFO), SIF_ALL};
491 GetScrollInfo(SB_HORZ, &si2);
493 InitView();
495 si1.nPos = int(float(si1.nPos)*m_fZoomFactor/oldzoom);
496 si2.nPos = int(float(si2.nPos)*m_fZoomFactor/oldzoom);
497 SetScrollPos (SB_VERT, si1.nPos);
498 SetScrollPos (SB_HORZ, si2.nPos);
501 Invalidate (FALSE);