RepositoryBrowser: Add drag handler
[TortoiseGit.git] / src / TortoiseGitBlame / LogListBlameAction.cpp
blob10cee6a69d7763bb5b23af1680d07c58fc66bb38
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2013, 2015-2016 - TortoiseGit
4 // Copyright (C) 2011-2013 Sven Strickroth <email@cs-ware.de>
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 "stdafx.h"
22 #include "GitBlameLogList.h"
23 #include "GitRev.h"
24 #include "TortoiseGitBlameDoc.h"
25 #include "TortoiseGitBlameView.h"
26 #include "MainFrm.h"
27 #include "CommonAppUtils.h"
29 IMPLEMENT_DYNAMIC(CGitBlameLogList, CHintListCtrl)
31 void CGitBlameLogList::hideUnimplementedCommands()
33 m_ContextMenuMask |= GetContextMenuBit(ID_BLAMEPREVIOUS) | GetContextMenuBit(ID_LOG);
34 hideFromContextMenu(
35 GetContextMenuBit(ID_COMPAREWITHPREVIOUS) |
36 GetContextMenuBit(ID_GNUDIFF1) |
37 GetContextMenuBit(ID_BLAMEPREVIOUS) |
38 GetContextMenuBit(ID_COPYCLIPBOARD) |
39 GetContextMenuBit(ID_COPYHASH) |
40 GetContextMenuBit(ID_EXPORT) |
41 GetContextMenuBit(ID_CREATE_BRANCH) |
42 GetContextMenuBit(ID_CREATE_TAG) |
43 GetContextMenuBit(ID_SWITCHTOREV) |
44 GetContextMenuBit(ID_LOG) |
45 GetContextMenuBit(ID_REPOBROWSE)
46 , true);
49 void CGitBlameLogList::GetParentHashes(GitRevLoglist* pRev, GIT_REV_LIST& parentHash)
51 std::vector<CTGitPath> paths;
52 GetPaths(pRev->m_CommitHash, paths);
54 std::set<int> parentNos;
55 GetParentNumbers(pRev, paths, parentNos);
57 for (auto it = parentNos.cbegin(); it != parentNos.cend(); ++it)
59 int parentNo = *it;
60 parentHash.push_back(pRev->m_ParentHash[parentNo]);
64 void RunTortoiseGitProcWithCurrentRev(const CString& command, const GitRev* pRev, const CString &path = g_Git.m_CurrentDir)
66 ASSERT(pRev);
67 CString procCmd;
68 procCmd.Format(L"/command:%s /path:\"%s\" /rev:%s", (LPCTSTR)command, (LPCTSTR)path, (LPCTSTR)pRev->m_CommitHash.ToString());
69 CCommonAppUtils::RunTortoiseGitProc(procCmd);
72 void CGitBlameLogList::ContextMenuAction(int cmd, int /*FirstSelect*/, int /*LastSelect*/, CMenu * /*menu*/)
74 POSITION pos = GetFirstSelectedItemPosition();
75 int indexNext = GetNextSelectedItem(pos);
76 if (indexNext < 0)
77 return;
78 CTortoiseGitBlameView *pView = DYNAMIC_DOWNCAST(CTortoiseGitBlameView,((CMainFrame*)::AfxGetApp()->GetMainWnd())->GetActiveView());
80 GitRevLoglist* pRev = &this->m_logEntries.GetGitRevAt(indexNext);
82 switch (cmd & 0xFFFF)
84 case ID_BLAMEPREVIOUS:
86 int index = (cmd >> 16) & 0xFFFF;
87 if (index > 0)
88 index -= 1;
90 CGitHash parentHash;
91 std::vector<CString> parentFilenames;
92 GetParentHash(pRev, index, parentHash, parentFilenames);
93 for (size_t i = 0; i < parentFilenames.size(); ++i)
95 CString procCmd = _T("/path:\"") + pView->ResolveCommitFile(parentFilenames[i]) + _T("\" ");
96 procCmd += _T(" /command:blame");
97 procCmd += _T(" /endrev:") + parentHash.ToString();
99 CCommonAppUtils::RunTortoiseGitProc(procCmd);
102 break;
103 case ID_GNUDIFF1: // fallthrough
104 case ID_COMPAREWITHPREVIOUS:
106 int index = (cmd >> 16) & 0xFFFF;
107 if (index > 0)
108 index -= 1;
110 CGitHash parentHash;
111 std::vector<CString> parentFilenames;
112 GetParentHash(pRev, index, parentHash, parentFilenames);
113 for (size_t i = 0; i < parentFilenames.size(); ++i)
115 CString procCmd = _T("/path:\"") + pView->ResolveCommitFile(parentFilenames[i]) + _T("\" ");
116 procCmd += _T(" /command:diff");
117 procCmd += _T(" /startrev:") + pRev->m_CommitHash.ToString();
118 procCmd += _T(" /endrev:") + parentHash.ToString();
119 if ((cmd & 0xFFFF) == ID_GNUDIFF1)
120 procCmd += _T(" /unified");
122 CCommonAppUtils::RunTortoiseGitProc(procCmd);
125 break;
126 case ID_COPYCLIPBOARD:
128 CopySelectionToClipBoard();
130 break;
131 case ID_COPYHASH:
133 CopySelectionToClipBoard(ID_COPY_HASH);
135 break;
136 case ID_EXPORT:
137 RunTortoiseGitProcWithCurrentRev(_T("export"), pRev);
138 break;
139 case ID_CREATE_BRANCH:
140 RunTortoiseGitProcWithCurrentRev(_T("branch"), pRev);
141 break;
142 case ID_CREATE_TAG:
143 RunTortoiseGitProcWithCurrentRev(_T("tag"), pRev);
144 break;
145 case ID_SWITCHTOREV:
146 RunTortoiseGitProcWithCurrentRev(_T("switch"), pRev);
147 break;
148 case ID_LOG:
150 CString procCmd;
151 procCmd.Format(L"/command:log /path:\"%s\" /endrev:%s /rev:%s", (LPCTSTR)((CMainFrame*)::AfxGetApp()->GetMainWnd())->GetActiveView()->GetDocument()->GetPathName(), (LPCTSTR)pRev->m_CommitHash.ToString(), (LPCTSTR)pRev->m_CommitHash.ToString());
152 CCommonAppUtils::RunTortoiseGitProc(procCmd);
154 break;
155 case ID_REPOBROWSE:
156 RunTortoiseGitProcWithCurrentRev(_T("repobrowser"), pRev, ((CMainFrame*)::AfxGetApp()->GetMainWnd())->GetActiveView()->GetDocument()->GetPathName());
157 break;
158 default:
159 //CMessageBox::Show(nullptr, _T("Have not implemented"), _T("TortoiseGit"), MB_OK);
160 break;
161 } // switch (cmd)
164 void CGitBlameLogList::GetPaths(const CGitHash& hash, std::vector<CTGitPath>& paths)
166 CTortoiseGitBlameView *pView = DYNAMIC_DOWNCAST(CTortoiseGitBlameView,((CMainFrame*)::AfxGetApp()->GetMainWnd())->GetActiveView());
167 if (pView)
170 std::set<CString> filenames;
171 int numberOfLines = pView->m_data.GetNumberOfLines();
172 for (int i = 0; i < numberOfLines; ++i)
174 if (pView->m_data.GetHash(i) == hash)
175 filenames.insert(pView->m_data.GetFilename(i));
177 for (auto it = filenames.cbegin(); it != filenames.cend(); ++it)
178 paths.emplace_back(*it);
180 if (paths.empty())
182 // in case the hash does not exist in the blame output but it exists in the log follow only the file
183 paths.push_back(pView->GetDocument()->m_GitPath);
188 void CGitBlameLogList::GetParentNumbers(GitRevLoglist* pRev, const std::vector<CTGitPath>& paths, std::set<int>& parentNos)
190 if (pRev->m_ParentHash.empty())
192 if (pRev->GetParentFromHash(pRev->m_CommitHash))
193 MessageBox(pRev->GetLastErr(), _T("TortoiseGit"), MB_ICONERROR);
196 GIT_REV_LIST allParentHash;
197 CGitLogListBase::GetParentHashes(pRev, allParentHash);
201 const CTGitPathList& files = pRev->GetFiles(nullptr);
202 for (int j=0, j_size = files.GetCount(); j < j_size; ++j)
204 const CTGitPath &file = files[j];
205 for (auto it=paths.cbegin(); it != paths.cend(); ++it)
207 const CTGitPath& path = *it;
208 if (file.IsEquivalentTo(path))
210 if (!(file.m_ParentNo & MERGE_MASK))
212 int action = file.m_Action;
213 // ignore (action & CTGitPath::LOGACTIONS_ADDED), as then there is nothing to blame/diff
214 // ignore (action & CTGitPath::LOGACTIONS_DELETED), should never happen as the file must exist
215 if (action & (CTGitPath::LOGACTIONS_MODIFIED | CTGitPath::LOGACTIONS_REPLACED))
217 int parentNo = file.m_ParentNo & PARENT_MASK;
218 if (parentNo >= 0 && (size_t)parentNo < pRev->m_ParentHash.size())
219 parentNos.insert(parentNo);
226 catch (const char* msg)
228 MessageBox(_T("Could not get files of parents.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
232 void CGitBlameLogList::GetParentHash(GitRevLoglist* pRev, int index, CGitHash& parentHash, std::vector<CString>& parentFilenames)
234 std::vector<CTGitPath> paths;
235 GetPaths(pRev->m_CommitHash, paths);
237 std::set<int> parentNos;
238 GetParentNumbers(pRev, paths, parentNos);
240 int parentNo = 0;
242 int i = 0;
243 for (auto it = parentNos.cbegin(); it != parentNos.cend(); ++it, ++i)
245 if (i == index)
247 parentNo = *it;
248 break;
252 parentHash = pRev->m_ParentHash[parentNo];
256 const CTGitPathList& files = pRev->GetFiles(nullptr);
257 for (int j = 0, j_size = files.GetCount(); j < j_size; ++j)
259 const CTGitPath &file = files[j];
260 for (auto it = paths.cbegin(); it != paths.cend(); ++it)
262 const CTGitPath& path = *it;
263 if (file.IsEquivalentTo(path))
265 if (!(file.m_ParentNo & MERGE_MASK))
267 int action = file.m_Action;
268 // ignore (action & CTGitPath::LOGACTIONS_ADDED), as then there is nothing to blame/diff
269 // ignore (action & CTGitPath::LOGACTIONS_DELETED), should never happen as the file must exist
270 if (action & (CTGitPath::LOGACTIONS_MODIFIED | CTGitPath::LOGACTIONS_REPLACED))
272 if (parentNo == (file.m_ParentNo & PARENT_MASK) && (size_t)parentNo < pRev->m_ParentHash.size())
273 parentFilenames.push_back( (action & CTGitPath::LOGACTIONS_REPLACED) ? file.GetGitOldPathString() : file.GetGitPathString());
280 catch (const char* msg)
282 MessageBox(_T("Could not get files of parents.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);