Don't import ogdf namespace
[TortoiseGit.git] / src / TortoiseProc / RevisionGraph / RevisionGraphDlgFunc.cpp
blob66dddbcecbb683b919cb2fbff020b4f3bd52e3b2
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2011 - TortoiseSVN
4 // Copyright (C) 2012-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 <gdiplus.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 ".\revisiongraphwnd.h"
30 //#include "CachedLogInfo.h"
31 //#include "RevisionGraph/IRevisionGraphLayout.h"
32 //#include "RevisionGraph/FullGraphBuilder.h"
33 //#include "RevisionGraph/FullGraphFinalizer.h"
34 //#include "RevisionGraph/VisibleGraphBuilder.h"
35 //#include "RevisionGraph/StandardLayout.h"
36 //#include "RevisionGraph/ShowWC.h"
37 //#include "RevisionGraph/ShowWCModification.h"
38 #include "DPIAware.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;
48 void CRevisionGraphWnd::InitView()
50 m_bIsCanvasMove = false;
52 SetScrollbars();
55 void CRevisionGraphWnd::BuildPreview()
57 m_Preview.DeleteObject();
58 if (!m_bShowOverview)
59 return;
61 // is there a point in drawing this at all?
63 int nodeCount = this->m_Graph.numberOfNodes();
64 if ((nodeCount > REVGRAPH_PREVIEW_MAX_NODES) || (nodeCount == 0))
65 return;
67 float origZoom = m_fZoomFactor;
69 CRect clientRect = GetClientRect();
70 CSize preViewSize(max(CDPIAware::Instance().ScaleX(REVGRAPH_PREVIEW_WIDTH), clientRect.Width() / 4),
71 max(CDPIAware::Instance().ScaleY(REVGRAPH_PREVIEW_HEIGHT), clientRect.Height() / 4));
73 // zoom the graph so that it is completely visible in the window
74 CRect graphRect = GetGraphRect();
75 float horzfact = float(graphRect.Width())/float(preViewSize.cx);
76 float vertfact = float(graphRect.Height())/float(preViewSize.cy);
77 m_previewZoom = min (DEFAULT_ZOOM, 1.0f/(max(horzfact, vertfact)));
79 // make sure the preview window has a minimal size
81 m_previewWidth = (int)min (max (graphRect.Width() * m_previewZoom, 30), preViewSize.cx);
82 m_previewHeight = (int)min (max (graphRect.Height() * m_previewZoom, 30), preViewSize.cy);
84 CClientDC ddc(this);
85 CDC dc;
86 if (!dc.CreateCompatibleDC(&ddc))
87 return;
89 m_Preview.CreateCompatibleBitmap(&ddc, m_previewWidth, m_previewHeight);
90 HBITMAP oldbm = (HBITMAP)dc.SelectObject (m_Preview);
92 // paint the whole graph
93 DoZoom (m_previewZoom, false);
94 CRect rect (0, 0, m_previewWidth, m_previewHeight);
95 GraphicsDevice dev;
96 dev.pDC = &dc;
97 DrawGraph(dev, rect, 0, 0, true);
99 // now we have a bitmap the size of the preview window
100 dc.SelectObject(oldbm);
101 dc.DeleteDC();
103 DoZoom (origZoom, false);
106 void CRevisionGraphWnd::SetScrollbar (int bar, int newPos, int clientMax, int graphMax)
108 SCROLLINFO ScrollInfo = {sizeof(SCROLLINFO), SIF_ALL};
109 GetScrollInfo (bar, &ScrollInfo);
111 clientMax = max(1, clientMax);
112 int oldHeight = ScrollInfo.nMax <= 0 ? clientMax : ScrollInfo.nMax;
113 int newHeight = static_cast<int>(graphMax * m_fZoomFactor);
114 int maxPos = max (0, newHeight - clientMax);
115 int pos = min (maxPos, newPos >= 0
116 ? newPos
117 : ScrollInfo.nPos * newHeight / oldHeight);
119 ScrollInfo.nPos = pos;
120 ScrollInfo.nMin = 0;
121 ScrollInfo.nMax = newHeight;
122 ScrollInfo.nPage = clientMax;
123 ScrollInfo.nTrackPos = pos;
125 SetScrollInfo(bar, &ScrollInfo);
128 void CRevisionGraphWnd::SetScrollbars (int nVert, int nHorz)
130 CRect clientrect = GetClientRect();
131 const CRect& pRect = GetGraphRect();
133 SetScrollbar (SB_VERT, nVert, clientrect.Height(), pRect.Height());
134 SetScrollbar (SB_HORZ, nHorz, clientrect.Width(), pRect.Width());
137 CRect CRevisionGraphWnd::GetGraphRect()
139 return m_GraphRect;
142 CRect CRevisionGraphWnd::GetClientRect()
144 CRect clientRect;
145 CWnd::GetClientRect (&clientRect);
146 return clientRect;
149 CRect CRevisionGraphWnd::GetWindowRect()
151 CRect windowRect;
152 CWnd::GetWindowRect (&windowRect);
153 return windowRect;
156 CRect CRevisionGraphWnd::GetViewRect()
158 CRect result;
159 result.UnionRect (GetClientRect(), GetGraphRect());
160 return result;
163 int CRevisionGraphWnd::GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
165 UINT num = 0; // number of image encoders
166 UINT size = 0; // size of the image encoder array in bytes
168 if (GetImageEncodersSize(&num, &size)!=Ok)
169 return -1;
170 if(size == 0)
171 return -1; // Failure
173 ImageCodecInfo* pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
174 if (!pImageCodecInfo)
175 return -1; // Failure
177 if (GetImageEncoders(num, size, pImageCodecInfo)==Ok)
179 for(UINT j = 0; j < num; ++j)
181 if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
183 *pClsid = pImageCodecInfo[j].Clsid;
184 free(pImageCodecInfo);
185 return j; // Success
190 free(pImageCodecInfo);
191 return -1; // Failure
194 bool CRevisionGraphWnd::FetchRevisionData
195 ( const CString& /*path*/
196 , CProgressDlg* /*progress*/
197 , ITaskbarList3 * /*pTaskbarList*/
198 , HWND /*hWnd*/)
200 this->m_LogCache.ClearAllParent();
201 this->m_logEntries.ClearAll();
202 CString range;
203 if (!m_ToRev.IsEmpty() && !m_FromRev.IsEmpty())
204 range.Format(L"%s..%s", (LPCTSTR)g_Git.FixBranchName(m_FromRev), (LPCTSTR)g_Git.FixBranchName(m_ToRev));
205 else if (!m_ToRev.IsEmpty())
206 range = m_ToRev;
207 else if (!m_FromRev.IsEmpty())
208 range = m_FromRev;
209 DWORD infomask = CGit::LOG_INFO_SIMPILFY_BY_DECORATION | (m_bCurrentBranch ? 0 : m_bLocalBranches ? CGit::LOG_INFO_LOCAL_BRANCHES : CGit::LOG_INFO_ALL_BRANCH);
210 m_logEntries.ParserFromLog(nullptr, 0, infomask, &range);
212 ReloadHashMap();
213 this->m_Graph.clear();
215 CArray<ogdf::node> nodes;
216 GraphicsDevice dev;
217 dev.pDC = this->GetDC();
218 dev.graphics = Graphics::FromHDC(dev.pDC->m_hDC);
219 dev.graphics->SetPageUnit (UnitPixel);
221 m_HeadNode = nullptr;
223 for (size_t i = 0; i < m_logEntries.size(); ++i)
225 auto nd = m_Graph.newNode();
226 nodes.Add(nd);
227 m_GraphAttr.width(nd)=100;
228 m_GraphAttr.height(nd)=20;
229 SetNodeRect(dev, &nd, m_logEntries[i], 0);
230 if (m_logEntries[i] == m_HeadHash)
231 m_HeadNode = nd;
234 for (size_t i = 0; i < m_logEntries.size(); ++i)
236 GitRev rev=m_logEntries.GetGitRevAt(i);
237 for (size_t j = 0; j < rev.m_ParentHash.size(); ++j)
239 if(m_logEntries.m_HashMap.find(rev.m_ParentHash[j]) == m_logEntries.m_HashMap.end())
241 TRACE(L"Can't found parent node");
242 //new parent node as new node
243 auto nd = this->m_Graph.newNode();
244 m_Graph.newEdge(nodes[i], nd);
245 m_logEntries.push_back(rev.m_ParentHash[j]);
246 m_logEntries.m_HashMap[rev.m_ParentHash[j]] = (int)m_logEntries.size() -1;
247 nodes.Add(nd);
248 SetNodeRect(dev, &nd, rev.m_ParentHash[j], 0);
250 }else
252 TRACE(L"edge %d - %d\n",i, m_logEntries.m_HashMap[rev.m_ParentHash[j]]);
253 m_Graph.newEdge(nodes[i], nodes[m_logEntries.m_HashMap[rev.m_ParentHash[j]]]);
258 //this->m_OHL.layerDistance(30.0);
259 //this->m_OHL.nodeDistance(25.0);
260 //this->m_OHL.weightBalancing(0.8);
262 m_SugiyamLayout.call(m_GraphAttr);
264 ogdf::node v;
265 double xmax = 0;
266 double ymax = 0;
267 forall_nodes(v,m_Graph)
269 double x = m_GraphAttr.x(v) + m_GraphAttr.width(v)/2;
270 double y = m_GraphAttr.y(v) + m_GraphAttr.height(v)/2;
271 if(x>xmax)
272 xmax = x;
273 if(y>ymax)
274 ymax = y;
277 this->m_GraphRect.top=m_GraphRect.left=0;
278 m_GraphRect.bottom = (LONG)ymax;
279 m_GraphRect.right = (LONG)xmax;
281 return true;
284 bool CRevisionGraphWnd::AnalyzeRevisionData()
286 #if 0
287 CSyncPointer<const CFullGraph> fullGraph (m_state.GetFullGraph());
288 if (fullGraph.get() != nullptr && (fullGraph->GetNodeCount() > 0))
290 // filter graph
292 CSyncPointer<CAllRevisionGraphOptions> options (m_state.GetOptions());
293 options->Prepare();
295 auto visibleGraph = std::make_unique<CVisibleGraph>();
296 CVisibleGraphBuilder builder ( *fullGraph
297 , *visibleGraph
298 , options->GetCopyFilterOptions());
299 builder.Run();
300 options->GetModificationOptions().Apply (visibleGraph.get());
302 index_t index = 0;
303 for (size_t i = 0, count = visibleGraph->GetRootCount(); i < count; ++i)
304 index = visibleGraph->GetRoot (i)->InitIndex (index);
306 // layout nodes
308 auto newLayout = std::make_unique<CStandardLayout>
309 ( new CStandardLayout ( m_state.GetFullHistory()->GetCache()
310 , visibleGraph.get()
311 , m_state.GetFullHistory()->GetWCInfo()));
312 options->GetLayoutOptions().Apply (newLayout.get());
313 newLayout->Finalize();
315 // switch state
317 m_state.SetAnalysisResult (visibleGraph, newLayout);
320 return m_state.GetNodes().get() != nullptr;
321 #endif
322 return true;
325 bool CRevisionGraphWnd::IsUpdateJobRunning() const
327 return (updateJob.get() != nullptr) && !updateJob->IsDone();
330 bool CRevisionGraphWnd::GetShowOverview() const
332 return m_bShowOverview;
335 void CRevisionGraphWnd::SetShowOverview (bool value)
337 m_bShowOverview = value;
338 if (m_bShowOverview)
339 BuildPreview();
341 #if 0
342 void CRevisionGraphWnd::GetSelected
343 ( const CVisibleGraphNode* node
344 , bool head
345 , CTGitPath& path
346 , GitRev& rev
347 , GitRev& peg)
349 CString repoRoot = m_state.GetRepositoryRoot();
351 // get path and revision
353 path.SetFromSVN (repoRoot + CUnicodeUtils::GetUnicode (node->GetPath().GetPath().c_str()));
354 rev = head ? GitRev::REV_HEAD : node->GetRevision();
356 // handle 'modified WC' node
358 if (node->GetClassification().Is (CNodeClassification::IS_MODIFIED_WC))
360 path.SetFromUnknown (m_sPath);
361 rev = GitRev::REV_WC;
363 // don't set peg, if we aren't the first node
364 // (i.e. would not be valid for node1)
366 if (node == m_SelectedEntry1)
367 peg = GitRev::REV_WC;
369 else
371 // set head, if still necessary
373 if (head && !peg.IsValid())
374 peg = node->GetRevision();
377 #endif
379 CString CRevisionGraphWnd::GetFriendRefName(ogdf::node v)
381 if (!v)
382 return CString();
383 CGitHash hash = this->m_logEntries[v->index()];
384 if(this->m_HashMap.find(hash) == m_HashMap.end())
385 return hash.ToString();
386 else if (m_HashMap[hash].empty())
387 return hash.ToString();
388 else if(this->m_HashMap[hash][0].IsEmpty())
389 return hash.ToString();
390 else
391 return m_HashMap[hash][0];
394 STRING_VECTOR CRevisionGraphWnd::GetFriendRefNames(ogdf::node v, const CString* exclude, CGit::REF_TYPE* onlyRefType)
396 if (!v)
397 return STRING_VECTOR();
398 CGitHash hash = m_logEntries[v->index()];
399 if (m_HashMap.find(hash) == m_HashMap.end())
400 return STRING_VECTOR();
401 else if (m_HashMap[hash].empty())
402 return STRING_VECTOR();
403 else if (m_HashMap[hash][0].IsEmpty())
404 return STRING_VECTOR();
405 else
407 STRING_VECTOR &all = m_HashMap[hash];
408 STRING_VECTOR list;
409 for (size_t i = 0; i < all.size(); ++i)
411 CGit::REF_TYPE refType;
412 CString shortName = CGit::GetShortName(all[i], &refType);
413 if (exclude && *exclude == shortName)
414 continue;
415 if (!onlyRefType)
416 list.push_back(all[i]);
417 else if (*onlyRefType == refType)
418 list.push_back(shortName);
420 return list;
424 void CRevisionGraphWnd::CompareRevs(const CString& revTo)
426 ASSERT(m_SelectedEntry1);
427 ASSERT(!revTo.IsEmpty() || m_SelectedEntry2);
429 bool alternativeTool = !!(GetAsyncKeyState(VK_SHIFT) & 0x8000);
431 CString sCmd;
433 sCmd.Format(L"/command:showcompare %s /revision1:%s /revision2:%s",
434 this->m_sPath.IsEmpty() ? L"" : (LPCTSTR)(L"/path:\"" + this->m_sPath + L'"'),
435 (LPCTSTR)GetFriendRefName(m_SelectedEntry1),
436 !revTo.IsEmpty() ? (LPCTSTR)revTo : (LPCTSTR)GetFriendRefName(m_SelectedEntry2));
438 if (alternativeTool)
439 sCmd += L" /alternative";
441 CAppUtils::RunTortoiseGitProc(sCmd);
444 void CRevisionGraphWnd::UnifiedDiffRevs(bool bHead)
446 ASSERT(m_SelectedEntry1);
447 ASSERT(bHead || m_SelectedEntry2);
449 bool alternativeTool = !!(GetAsyncKeyState(VK_SHIFT) & 0x8000);
450 CAppUtils::StartShowUnifiedDiff(m_hWnd, CString(), GetFriendRefName(m_SelectedEntry1), CString(),
451 bHead ? L"HEAD" : GetFriendRefName(m_SelectedEntry2),
452 alternativeTool);
455 void CRevisionGraphWnd::DoZoom (float fZoomFactor, bool updateScrollbars)
457 float oldzoom = m_fZoomFactor;
458 m_fZoomFactor = fZoomFactor;
460 m_nFontSize = max(1, int(DEFAULT_ZOOM_FONT * fZoomFactor));
461 if (m_nFontSize < SMALL_ZOOM_FONT_THRESHOLD)
462 m_nFontSize = min (SMALL_ZOOM_FONT_THRESHOLD, int(SMALL_ZOOM_FONT * fZoomFactor));
464 for (int i = 0; i < MAXFONTS; ++i)
466 if (m_apFonts[i])
468 m_apFonts[i]->DeleteObject();
469 delete m_apFonts[i];
471 m_apFonts[i] = nullptr;
474 if (updateScrollbars)
476 SCROLLINFO si1 = {sizeof(SCROLLINFO), SIF_ALL};
477 GetScrollInfo(SB_VERT, &si1);
478 SCROLLINFO si2 = {sizeof(SCROLLINFO), SIF_ALL};
479 GetScrollInfo(SB_HORZ, &si2);
481 InitView();
483 si1.nPos = int(float(si1.nPos)*m_fZoomFactor/oldzoom);
484 si2.nPos = int(float(si2.nPos)*m_fZoomFactor/oldzoom);
485 SetScrollPos (SB_VERT, si1.nPos);
486 SetScrollPos (SB_HORZ, si2.nPos);
489 Invalidate (FALSE);