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.
21 #include "TortoiseProc.h"
23 #include "Revisiongraphdlg.h"
26 #include "UnicodeUtils.h"
33 static char THIS_FILE
[] = __FILE__
;
36 using namespace Gdiplus
;
38 void CRevisionGraphWnd::InitView()
40 m_bIsCanvasMove
= false;
45 void CRevisionGraphWnd::BuildPreview()
47 m_Preview
.DeleteObject();
51 // is there a point in drawing this at all?
53 int nodeCount
= this->m_Graph
.numberOfNodes();
54 if ((nodeCount
> REVGRAPH_PREVIEW_MAX_NODES
) || (nodeCount
== 0))
57 float origZoom
= m_fZoomFactor
;
59 CRect clientRect
= GetClientRect();
60 CSize
preViewSize(max(CDPIAware::Instance().ScaleX(REVGRAPH_PREVIEW_WIDTH
), clientRect
.Width() / 4),
61 max(CDPIAware::Instance().ScaleY(REVGRAPH_PREVIEW_HEIGHT
), clientRect
.Height() / 4));
63 // zoom the graph so that it is completely visible in the window
64 CRect graphRect
= GetGraphRect();
65 float horzfact
= float(graphRect
.Width())/float(preViewSize
.cx
);
66 float vertfact
= float(graphRect
.Height())/float(preViewSize
.cy
);
67 m_previewZoom
= min (DEFAULT_ZOOM
, 1.0f
/(max(horzfact
, vertfact
)));
69 // make sure the preview window has a minimal size
71 m_previewWidth
= (int)min (max (graphRect
.Width() * m_previewZoom
, 30), preViewSize
.cx
);
72 m_previewHeight
= (int)min (max (graphRect
.Height() * m_previewZoom
, 30), preViewSize
.cy
);
76 if (!dc
.CreateCompatibleDC(&ddc
))
79 m_Preview
.CreateCompatibleBitmap(&ddc
, m_previewWidth
, m_previewHeight
);
80 HBITMAP oldbm
= (HBITMAP
)dc
.SelectObject (m_Preview
);
82 // paint the whole graph
83 DoZoom (m_previewZoom
, false);
84 CRect
rect (0, 0, m_previewWidth
, m_previewHeight
);
87 DrawGraph(dev
, rect
, 0, 0, true);
89 // now we have a bitmap the size of the preview window
90 dc
.SelectObject(oldbm
);
93 DoZoom (origZoom
, false);
96 void CRevisionGraphWnd::SetScrollbar (int bar
, int newPos
, int clientMax
, int graphMax
)
98 SCROLLINFO ScrollInfo
= {sizeof(SCROLLINFO
), SIF_ALL
};
99 GetScrollInfo (bar
, &ScrollInfo
);
101 clientMax
= max(1, clientMax
);
102 int oldHeight
= ScrollInfo
.nMax
<= 0 ? clientMax
: ScrollInfo
.nMax
;
103 int newHeight
= static_cast<int>(graphMax
* m_fZoomFactor
);
104 int maxPos
= max (0, newHeight
- clientMax
);
105 int pos
= min (maxPos
, newPos
>= 0
107 : ScrollInfo
.nPos
* newHeight
/ oldHeight
);
109 ScrollInfo
.nPos
= pos
;
111 ScrollInfo
.nMax
= newHeight
;
112 ScrollInfo
.nPage
= clientMax
;
113 ScrollInfo
.nTrackPos
= pos
;
115 SetScrollInfo(bar
, &ScrollInfo
);
118 void CRevisionGraphWnd::SetScrollbars (int nVert
, int nHorz
)
120 CRect clientrect
= GetClientRect();
121 const CRect
& pRect
= GetGraphRect();
123 SetScrollbar (SB_VERT
, nVert
, clientrect
.Height(), pRect
.Height());
124 SetScrollbar (SB_HORZ
, nHorz
, clientrect
.Width(), pRect
.Width());
127 CRect
CRevisionGraphWnd::GetGraphRect()
132 CRect
CRevisionGraphWnd::GetClientRect()
135 CWnd::GetClientRect (&clientRect
);
139 CRect
CRevisionGraphWnd::GetWindowRect()
142 CWnd::GetWindowRect (&windowRect
);
146 CRect
CRevisionGraphWnd::GetViewRect()
149 result
.UnionRect (GetClientRect(), GetGraphRect());
153 int CRevisionGraphWnd::GetEncoderClsid(const WCHAR
* format
, CLSID
* pClsid
)
155 UINT num
= 0; // number of image encoders
156 UINT size
= 0; // size of the image encoder array in bytes
158 if (GetImageEncodersSize(&num
, &size
)!=Ok
)
161 return -1; // Failure
163 ImageCodecInfo
* pImageCodecInfo
= (ImageCodecInfo
*)(malloc(size
));
164 if (!pImageCodecInfo
)
165 return -1; // Failure
167 if (GetImageEncoders(num
, size
, pImageCodecInfo
)==Ok
)
169 for(UINT j
= 0; j
< num
; ++j
)
171 if( wcscmp(pImageCodecInfo
[j
].MimeType
, format
) == 0 )
173 *pClsid
= pImageCodecInfo
[j
].Clsid
;
174 free(pImageCodecInfo
);
180 free(pImageCodecInfo
);
181 return -1; // Failure
184 bool CRevisionGraphWnd::FetchRevisionData
185 ( const CString
& /*path*/
186 , CProgressDlg
* /*progress*/
187 , ITaskbarList3
* /*pTaskbarList*/
190 this->m_LogCache
.ClearAllParent();
191 this->m_logEntries
.ClearAll();
193 if (!m_ToRev
.IsEmpty() && !m_FromRev
.IsEmpty())
194 range
.Format(L
"%s..%s", (LPCTSTR
)g_Git
.FixBranchName(m_FromRev
), (LPCTSTR
)g_Git
.FixBranchName(m_ToRev
));
195 else if (!m_ToRev
.IsEmpty())
197 else if (!m_FromRev
.IsEmpty())
199 DWORD infomask
= CGit::LOG_INFO_SIMPILFY_BY_DECORATION
| (m_bCurrentBranch
? 0 : m_bLocalBranches
? CGit::LOG_INFO_LOCAL_BRANCHES
: CGit::LOG_INFO_ALL_BRANCH
);
200 m_logEntries
.ParserFromLog(nullptr, 0, infomask
, &range
);
203 this->m_Graph
.clear();
205 CArray
<ogdf::node
> nodes
;
207 dev
.pDC
= this->GetDC();
208 dev
.graphics
= Graphics::FromHDC(dev
.pDC
->m_hDC
);
209 dev
.graphics
->SetPageUnit (UnitPixel
);
211 m_HeadNode
= nullptr;
213 for (size_t i
= 0; i
< m_logEntries
.size(); ++i
)
215 auto nd
= m_Graph
.newNode();
217 m_GraphAttr
.width(nd
)=100;
218 m_GraphAttr
.height(nd
)=20;
219 SetNodeRect(dev
, &nd
, m_logEntries
[i
], 0);
220 if (m_logEntries
[i
] == m_HeadHash
)
224 for (size_t i
= 0; i
< m_logEntries
.size(); ++i
)
226 GitRev rev
=m_logEntries
.GetGitRevAt(i
);
227 for (size_t j
= 0; j
< rev
.m_ParentHash
.size(); ++j
)
229 if(m_logEntries
.m_HashMap
.find(rev
.m_ParentHash
[j
]) == m_logEntries
.m_HashMap
.end())
231 TRACE(L
"Can't found parent node");
232 //new parent node as new node
233 auto nd
= this->m_Graph
.newNode();
234 m_Graph
.newEdge(nodes
[i
], nd
);
235 m_logEntries
.push_back(rev
.m_ParentHash
[j
]);
236 m_logEntries
.m_HashMap
[rev
.m_ParentHash
[j
]] = (int)m_logEntries
.size() -1;
238 SetNodeRect(dev
, &nd
, rev
.m_ParentHash
[j
], 0);
242 TRACE(L
"edge %d - %d\n",i
, m_logEntries
.m_HashMap
[rev
.m_ParentHash
[j
]]);
243 m_Graph
.newEdge(nodes
[i
], nodes
[m_logEntries
.m_HashMap
[rev
.m_ParentHash
[j
]]]);
248 //this->m_OHL.layerDistance(30.0);
249 //this->m_OHL.nodeDistance(25.0);
250 //this->m_OHL.weightBalancing(0.8);
252 m_SugiyamLayout
.call(m_GraphAttr
);
257 forall_nodes(v
,m_Graph
)
259 double x
= m_GraphAttr
.x(v
) + m_GraphAttr
.width(v
)/2;
260 double y
= m_GraphAttr
.y(v
) + m_GraphAttr
.height(v
)/2;
267 this->m_GraphRect
.top
=m_GraphRect
.left
=0;
268 m_GraphRect
.bottom
= (LONG
)ymax
;
269 m_GraphRect
.right
= (LONG
)xmax
;
274 bool CRevisionGraphWnd::IsUpdateJobRunning() const
276 return (updateJob
.get() != nullptr) && !updateJob
->IsDone();
279 bool CRevisionGraphWnd::GetShowOverview() const
281 return m_bShowOverview
;
284 void CRevisionGraphWnd::SetShowOverview (bool value
)
286 m_bShowOverview
= value
;
291 void CRevisionGraphWnd::GetSelected
292 ( const CVisibleGraphNode
* node
298 CString repoRoot
= m_state
.GetRepositoryRoot();
300 // get path and revision
302 path
.SetFromSVN (repoRoot
+ CUnicodeUtils::GetUnicode (node
->GetPath().GetPath().c_str()));
303 rev
= head
? GitRev::REV_HEAD
: node
->GetRevision();
305 // handle 'modified WC' node
307 if (node
->GetClassification().Is (CNodeClassification::IS_MODIFIED_WC
))
309 path
.SetFromUnknown (m_sPath
);
310 rev
= GitRev::REV_WC
;
312 // don't set peg, if we aren't the first node
313 // (i.e. would not be valid for node1)
315 if (node
== m_SelectedEntry1
)
316 peg
= GitRev::REV_WC
;
320 // set head, if still necessary
322 if (head
&& !peg
.IsValid())
323 peg
= node
->GetRevision();
328 CString
CRevisionGraphWnd::GetFriendRefName(ogdf::node v
)
332 CGitHash hash
= this->m_logEntries
[v
->index()];
333 if(this->m_HashMap
.find(hash
) == m_HashMap
.end())
334 return hash
.ToString();
335 else if (m_HashMap
[hash
].empty())
336 return hash
.ToString();
337 else if(this->m_HashMap
[hash
][0].IsEmpty())
338 return hash
.ToString();
340 return m_HashMap
[hash
][0];
343 STRING_VECTOR
CRevisionGraphWnd::GetFriendRefNames(ogdf::node v
, const CString
* exclude
, CGit::REF_TYPE
* onlyRefType
)
346 return STRING_VECTOR();
347 CGitHash hash
= m_logEntries
[v
->index()];
348 if (m_HashMap
.find(hash
) == m_HashMap
.end())
349 return STRING_VECTOR();
350 else if (m_HashMap
[hash
].empty())
351 return STRING_VECTOR();
352 else if (m_HashMap
[hash
][0].IsEmpty())
353 return STRING_VECTOR();
356 STRING_VECTOR
&all
= m_HashMap
[hash
];
358 for (size_t i
= 0; i
< all
.size(); ++i
)
360 CGit::REF_TYPE refType
;
361 CString shortName
= CGit::GetShortName(all
[i
], &refType
);
362 if (exclude
&& *exclude
== shortName
)
365 list
.push_back(all
[i
]);
366 else if (*onlyRefType
== refType
)
367 list
.push_back(shortName
);
373 void CRevisionGraphWnd::CompareRevs(const CString
& revTo
)
375 ASSERT(m_SelectedEntry1
);
376 ASSERT(!revTo
.IsEmpty() || m_SelectedEntry2
);
378 bool alternativeTool
= !!(GetAsyncKeyState(VK_SHIFT
) & 0x8000);
382 sCmd
.Format(L
"/command:showcompare %s /revision1:%s /revision2:%s",
383 this->m_sPath
.IsEmpty() ? L
"" : (LPCTSTR
)(L
"/path:\"" + this->m_sPath
+ L
'"'),
384 (LPCTSTR
)GetFriendRefName(m_SelectedEntry1
),
385 !revTo
.IsEmpty() ? (LPCTSTR
)revTo
: (LPCTSTR
)GetFriendRefName(m_SelectedEntry2
));
388 sCmd
+= L
" /alternative";
390 CAppUtils::RunTortoiseGitProc(sCmd
);
393 void CRevisionGraphWnd::UnifiedDiffRevs(bool bHead
)
395 ASSERT(m_SelectedEntry1
);
396 ASSERT(bHead
|| m_SelectedEntry2
);
398 bool alternativeTool
= !!(GetAsyncKeyState(VK_SHIFT
) & 0x8000);
399 CAppUtils::StartShowUnifiedDiff(m_hWnd
, CString(), GetFriendRefName(m_SelectedEntry1
), CString(),
400 bHead
? L
"HEAD" : GetFriendRefName(m_SelectedEntry2
),
404 void CRevisionGraphWnd::DoZoom (float fZoomFactor
, bool updateScrollbars
)
406 float oldzoom
= m_fZoomFactor
;
407 m_fZoomFactor
= fZoomFactor
;
409 m_nFontSize
= max(1, int(DEFAULT_ZOOM_FONT
* fZoomFactor
));
410 if (m_nFontSize
< SMALL_ZOOM_FONT_THRESHOLD
)
411 m_nFontSize
= min (SMALL_ZOOM_FONT_THRESHOLD
, int(SMALL_ZOOM_FONT
* fZoomFactor
));
413 for (int i
= 0; i
< MAXFONTS
; ++i
)
417 m_apFonts
[i
]->DeleteObject();
420 m_apFonts
[i
] = nullptr;
423 if (updateScrollbars
)
425 SCROLLINFO si1
= {sizeof(SCROLLINFO
), SIF_ALL
};
426 GetScrollInfo(SB_VERT
, &si1
);
427 SCROLLINFO si2
= {sizeof(SCROLLINFO
), SIF_ALL
};
428 GetScrollInfo(SB_HORZ
, &si2
);
432 si1
.nPos
= int(float(si1
.nPos
)*m_fZoomFactor
/oldzoom
);
433 si2
.nPos
= int(float(si2
.nPos
)*m_fZoomFactor
/oldzoom
);
434 SetScrollPos (SB_VERT
, si1
.nPos
);
435 SetScrollPos (SB_HORZ
, si2
.nPos
);