Allow to clear filters with escape
[TortoiseGit.git] / src / TortoiseProc / CommitIsOnRefsDlg.cpp
blob32b10ddc48b462dd055fb2c34e23d77aeb14aaa0
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2016 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "stdafx.h"
20 #include "TortoiseProc.h"
21 #include "CommitIsOnRefsDlg.h"
22 #include "StringUtils.h"
23 #include "BrowseRefsDlg.h"
24 #include "RefLogDlg.h"
25 #include "LogDlg.h"
26 #include "LoglistUtils.h"
27 #include "AppUtils.h"
28 #include "FileDiffDlg.h"
30 // CCommitIsOnRefsDlg dialog
32 UINT CCommitIsOnRefsDlg::WM_GETTINGREFSFINISHED = RegisterWindowMessage(_T("TORTOISEGIT_CommitIsOnRefs_GETTINGREFSFINISHED"));
34 IMPLEMENT_DYNAMIC(CCommitIsOnRefsDlg, CResizableStandAloneDialog)
36 CCommitIsOnRefsDlg::CCommitIsOnRefsDlg(CWnd* pParent /*=nullptr*/)
37 : CResizableStandAloneDialog(CCommitIsOnRefsDlg::IDD, pParent)
38 , m_bThreadRunning(FALSE)
39 , m_bRefsLoaded(false)
40 , m_bHasWC(true)
44 CCommitIsOnRefsDlg::~CCommitIsOnRefsDlg()
48 void CCommitIsOnRefsDlg::DoDataExchange(CDataExchange* pDX)
50 CDialog::DoDataExchange(pDX);
51 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_cRefList);
52 DDX_Control(pDX, IDC_FILTER, m_cFilter);
53 DDX_Control(pDX, IDC_COMMIT, m_cRevEdit);
54 DDX_Control(pDX, IDC_SELREF, m_cSelRevBtn);
57 BEGIN_MESSAGE_MAP(CCommitIsOnRefsDlg, CResizableStandAloneDialog)
58 ON_BN_CLICKED(IDC_SELREF, OnBnClickedSelRevBtn)
59 ON_BN_CLICKED(IDC_LOG, OnBnClickedShowLog)
60 ON_EN_CHANGE(IDC_FILTER, OnEnChangeEditFilter)
61 ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_REF_LEAFS, OnItemChangedListRefs)
62 ON_MESSAGE(ENAC_UPDATE, OnEnChangeCommit)
63 ON_WM_TIMER()
64 ON_WM_CONTEXTMENU()
65 ON_WM_SETCURSOR()
66 ON_REGISTERED_MESSAGE(WM_GETTINGREFSFINISHED, OnGettingRefsFinished)
67 END_MESSAGE_MAP()
69 // CCommitIsOnRefsDlg message handlers
71 void CCommitIsOnRefsDlg::OnCancel()
73 if (m_bThreadRunning)
74 return;
76 __super::OnCancel();
79 BOOL CCommitIsOnRefsDlg::OnInitDialog()
81 __super::OnInitDialog();
83 AddAnchor(IDC_FILTER, BOTTOM_LEFT, BOTTOM_RIGHT);
84 AddAnchor(IDC_LABEL_FILTER, BOTTOM_LEFT);
85 AddAnchor(IDC_SELREF, TOP_RIGHT);
86 AddAnchor(IDC_COMMIT, TOP_LEFT, TOP_RIGHT);
87 AddAnchor(IDC_STATIC_SUBJECT, TOP_LEFT, TOP_RIGHT);
88 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
89 AddAnchor(IDC_LOG, TOP_RIGHT);
90 AddOthersToAnchor();
92 m_bHasWC = !GitAdminDir::IsBareRepo(g_Git.m_CurrentDir);
94 m_cSelRevBtn.m_bRightArrow = TRUE;
95 m_cSelRevBtn.m_bDefaultClick = FALSE;
96 m_cSelRevBtn.m_bMarkDefault = FALSE;
97 m_cSelRevBtn.m_bShowCurrentItem = FALSE;
98 m_cSelRevBtn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFBROWSE)));
99 m_cSelRevBtn.AddEntry(CString(MAKEINTRESOURCE(IDS_LOG)));
100 m_cSelRevBtn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFLOG)));
102 EnableSaveRestore(L"CommitIsOnRefsDlg");
104 CImageList* imagelist = new CImageList();
105 imagelist->Create(IDB_BITMAP_REFTYPE, 16, 3, RGB(255, 255, 255));
106 m_cRefList.SetImageList(imagelist, LVSIL_SMALL);
108 CRect rect;
109 m_cRefList.GetClientRect(&rect);
110 m_cRefList.InsertColumn(0, L"Ref", 0, rect.Width() - 50);
112 m_cFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
113 m_cFilter.SetInfoIcon(IDI_FILTEREDIT);
115 m_cRevEdit.Init();
116 m_cRevEdit.SetWindowText(m_Rev);
118 StartGetRefsThread();
120 m_cRevEdit.SetFocus();
122 return FALSE;
125 void CCommitIsOnRefsDlg::AddToList()
127 m_cRefList.DeleteAllItems();
129 CString filter;
130 m_cFilter.GetWindowText(filter);
132 int item = 0;
133 for (size_t i = 0; i < m_RefList.size(); ++i)
135 int nImage = -1;
136 CString ref = m_RefList[i];
137 if (CStringUtils::StartsWith(ref, L"refs/tags/"))
138 nImage = 0;
139 else if (CStringUtils::StartsWith(ref, L"refs/remotes/"))
140 nImage = 2;
141 else if (CStringUtils::StartsWith(ref, L"refs/heads/"))
142 nImage = 1;
144 if (ref.Find(filter) >= 0)
145 m_cRefList.InsertItem(item++, ref, nImage);
148 if (item)
149 m_cRefList.ShowText(L"");
150 else
151 m_cRefList.ShowText(CString(MAKEINTRESOURCE(IDS_ERROR_NOREF)));
154 void CCommitIsOnRefsDlg::OnEnChangeEditFilter()
156 SetTimer(IDT_FILTER, 1000, nullptr);
159 void CCommitIsOnRefsDlg::OnTimer(UINT_PTR nIDEvent)
161 if (nIDEvent == IDT_FILTER)
163 KillTimer(IDT_FILTER);
164 AddToList();
166 else if (nIDEvent == IDT_INPUT)
168 KillTimer(IDT_INPUT);
169 StartGetRefsThread();
172 __super::OnTimer(nIDEvent);
175 void CCommitIsOnRefsDlg::OnBnClickedShowLog()
177 CString cmd;
178 cmd.Format(L"/command:log /rev:%s", (LPCTSTR)m_gitrev.m_CommitHash.ToString());
179 CAppUtils::RunTortoiseGitProc(cmd);
182 void CCommitIsOnRefsDlg::OnBnClickedSelRevBtn()
184 INT_PTR entry = m_cSelRevBtn.GetCurrentEntry();
185 if (entry == 0) /* Browse Refence*/
188 CString str = CBrowseRefsDlg::PickRef();
189 if (str.IsEmpty())
190 return;
192 if (FillRevFromString(str))
193 return;
195 m_cRevEdit.SetWindowText(str);
199 if (entry == 1) /*Log*/
201 CLogDlg dlg;
202 CString revision;
203 m_cRevEdit.GetWindowText(revision);
204 dlg.SetParams(CTGitPath(), CTGitPath(), revision, revision, 0);
205 dlg.SetSelect(true);
206 if (dlg.DoModal() == IDOK)
208 if (dlg.GetSelectedHash().empty())
209 return;
211 if (FillRevFromString(dlg.GetSelectedHash().at(0).ToString()))
212 return;
214 m_cRevEdit.SetWindowText(dlg.GetSelectedHash().at(0).ToString());
216 else
217 return;
220 if (entry == 2) /*RefLog*/
222 CRefLogDlg dlg;
223 if (dlg.DoModal() == IDOK)
225 if (FillRevFromString(dlg.m_SelectedHash))
226 return;
228 m_cRevEdit.SetWindowText(dlg.m_SelectedHash);
230 else
231 return;
234 StartGetRefsThread();
235 KillTimer(IDT_INPUT);
238 LRESULT CCommitIsOnRefsDlg::OnEnChangeCommit(WPARAM, LPARAM)
240 SetTimer(IDT_INPUT, 1000, nullptr);
241 return 0;
244 void CCommitIsOnRefsDlg::OnContextMenu(CWnd* pWnd, CPoint point)
246 if (!pWnd || pWnd != &m_cRefList)
247 return;
248 if (m_cRefList.GetSelectedCount() == 0)
249 return;
250 // if the context menu is invoked through the keyboard, we have to use
251 // a calculated position on where to anchor the menu on
252 if ((point.x == -1) && (point.y == -1))
254 CRect rect;
255 m_cRefList.GetItemRect(m_cRefList.GetSelectionMark(), &rect, LVIR_LABEL);
256 m_cRefList.ClientToScreen(&rect);
257 point = rect.CenterPoint();
259 CIconMenu popup;
260 if (popup.CreatePopupMenu())
262 STRING_VECTOR selectedRefs;
263 POSITION pos = m_cRefList.GetFirstSelectedItemPosition();
264 while (pos)
265 selectedRefs.push_back(m_cRefList.GetItemText(m_cRefList.GetNextSelectedItem(pos), 0));
266 bool needSep = false;
267 if (selectedRefs.size() == 2)
269 popup.AppendMenuIcon(eCmd_Diff, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF);
270 popup.AppendMenuIcon(eCmd_UnifiedDiff, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);
271 popup.AppendMenu(MF_SEPARATOR, NULL);
272 CString menu;
273 menu.Format(IDS_SHOWLOG_OF, (LPCTSTR)GetTwoSelectedRefs(selectedRefs, m_sLastSelected, L".."));
274 popup.AppendMenuIcon(eCmd_ViewLogRange, menu, IDI_LOG);
275 menu.Format(IDS_SHOWLOG_OF, (LPCTSTR)GetTwoSelectedRefs(selectedRefs, m_sLastSelected, L"..."));
276 popup.AppendMenuIcon(eCmd_ViewLogRangeReachableFromOnlyOne, menu, IDI_LOG);
277 needSep = true;
279 else if (selectedRefs.size() == 1)
281 popup.AppendMenuIcon(eCmd_ViewLog, IDS_FILEDIFF_LOG, IDI_LOG);
282 popup.AppendMenu(MF_SEPARATOR, NULL);
283 popup.AppendMenuIcon(eCmd_RepoBrowser, IDS_LOG_BROWSEREPO, IDI_REPOBROWSE);
284 if (m_bHasWC)
286 popup.AppendMenu(MF_SEPARATOR);
287 popup.AppendMenuIcon(eCmd_DiffWC, IDS_LOG_POPUP_COMPARE, IDI_DIFF);
289 needSep = true;
292 if (!selectedRefs.empty())
294 if (needSep)
295 popup.AppendMenu(MF_SEPARATOR, NULL);
297 popup.AppendMenuIcon(eCmd_Copy, IDS_SCIEDIT_COPY, IDI_COPYCLIP);
300 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this);
301 switch (cmd)
303 case eCmd_Diff:
305 CFileDiffDlg dlg;
306 dlg.SetDiff(
307 nullptr,
308 selectedRefs[0] + L"^{}",
309 selectedRefs[1] + L"^{}");
310 dlg.DoModal();
312 break;
313 case eCmd_UnifiedDiff:
314 CAppUtils::StartShowUnifiedDiff(GetSafeHwnd(), CTGitPath(), selectedRefs.at(0), CTGitPath(), selectedRefs.at(1), !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
315 break;
316 case eCmd_ViewLog:
318 CString sCmd = L"/command:log";
319 sCmd += L" /path:\"" + g_Git.m_CurrentDir + L"\" ";
320 sCmd += L" /endrev:" + selectedRefs.at(0);
321 CAppUtils::RunTortoiseGitProc(sCmd);
323 break;
324 case eCmd_ViewLogRange:
326 CString sCmd;
327 sCmd.Format(L"/command:log /path:\"%s\" /range:\"%s\"", (LPCTSTR)g_Git.m_CurrentDir, (LPCTSTR)GetTwoSelectedRefs(selectedRefs, m_sLastSelected, L".."));
328 CAppUtils::RunTortoiseGitProc(sCmd);
330 break;
331 case eCmd_ViewLogRangeReachableFromOnlyOne:
333 CString sCmd;
334 sCmd.Format(L"/command:log /path:\"%s\" /range:\"%s\"", (LPCTSTR)g_Git.m_CurrentDir, (LPCTSTR)GetTwoSelectedRefs(selectedRefs, m_sLastSelected, L"..."));
335 CAppUtils::RunTortoiseGitProc(sCmd);
337 break;
338 case eCmd_RepoBrowser:
339 CAppUtils::RunTortoiseGitProc(L"/command:repobrowser /path:\"" + g_Git.m_CurrentDir + L"\" /rev:" + selectedRefs[0]);
340 break;
341 case eCmd_Copy:
342 CopySelectionToClipboard();
343 break;
344 case eCmd_DiffWC:
346 CString sCmd;
347 sCmd.Format(_T("/command:showcompare /path:\"%s\" /revision1:%s /revision2:%s"), (LPCTSTR)g_Git.m_CurrentDir, (LPCTSTR)selectedRefs[0], (LPCTSTR)GitRev::GetWorkingCopy());
348 if (!!(GetAsyncKeyState(VK_SHIFT) & 0x8000))
349 sCmd += L" /alternative";
351 CAppUtils::RunTortoiseGitProc(sCmd);
353 break;
358 void CCommitIsOnRefsDlg::OnItemChangedListRefs(NMHDR* pNMHDR, LRESULT* pResult)
360 LPNMLISTVIEW pNMListView = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
361 *pResult = 0;
363 if (pNMListView->iItem >= 0 && m_RefList.size() > pNMListView->iItem && (pNMListView->uNewState & LVIS_SELECTED))
364 m_sLastSelected = m_RefList[pNMListView->iItem];
367 CString CCommitIsOnRefsDlg::GetTwoSelectedRefs(const STRING_VECTOR& selectedRefs, const CString& lastSelected, const CString& separator)
369 ASSERT(selectedRefs.size() == 2);
371 if (selectedRefs.at(0) == lastSelected)
372 return g_Git.StripRefName(selectedRefs.at(1)) + separator + g_Git.StripRefName(lastSelected);
373 else
374 return g_Git.StripRefName(selectedRefs.at(0)) + separator + g_Git.StripRefName(lastSelected);
377 void CCommitIsOnRefsDlg::StartGetRefsThread()
379 if (InterlockedExchange(&m_bThreadRunning, TRUE))
380 return;
382 KillTimer(IDT_INPUT);
384 SetDlgItemText(IDC_STATIC_SUBJECT, L"");
385 m_tooltips.DelTool(IDC_STATIC_SUBJECT);
387 DialogEnableWindow(IDC_LOG, FALSE);
388 DialogEnableWindow(IDC_SELREF, FALSE);
389 DialogEnableWindow(IDC_FILTER, FALSE);
391 m_RefList.clear();
392 m_cRefList.ShowText(CString(MAKEINTRESOURCE(IDS_STATUSLIST_BUSYMSG)));
393 m_cRefList.DeleteAllItems();
394 RefreshCursor();
396 m_cRevEdit.GetWindowText(m_Rev);
398 if (!AfxBeginThread(GetRefsThreadEntry, this))
400 InterlockedExchange(&m_bThreadRunning, FALSE);
401 CMessageBox::Show(GetSafeHwnd(), IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
405 UINT CCommitIsOnRefsDlg::GetRefsThreadEntry(LPVOID pVoid)
407 return ((CCommitIsOnRefsDlg*)pVoid)->GetRefsThread();
410 UINT CCommitIsOnRefsDlg::GetRefsThread()
412 if (!m_bRefsLoaded)
414 m_cRevEdit.RemoveSearchAll();
415 STRING_VECTOR refs;
416 g_Git.GetRefList(refs);
417 for (const auto& ref : refs)
418 m_cRevEdit.AddSearchString(ref);
419 m_bRefsLoaded = true;
422 if (m_Rev.IsEmpty() || m_gitrev.GetCommit(m_Rev))
424 SendMessage(WM_GETTINGREFSFINISHED);
426 InterlockedExchange(&m_bThreadRunning, FALSE);
427 return 0;
430 if (g_Git.GetRefsCommitIsOn(m_RefList, m_gitrev.m_CommitHash, true, true, CGit::BRANCH_ALL))
432 MessageBox(g_Git.GetGitLastErr(L"Could not get all refs."), L"TortoiseGit", MB_ICONERROR);
433 return 0;
435 SendMessage(WM_GETTINGREFSFINISHED);
437 InterlockedExchange(&m_bThreadRunning, FALSE);
438 return 0;
441 BOOL CCommitIsOnRefsDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
443 if (pWnd != &m_cRefList)
444 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
445 if (!m_bThreadRunning)
447 HCURSOR hCur = LoadCursor(nullptr, IDC_ARROW);
448 SetCursor(hCur);
449 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
451 HCURSOR hCur = LoadCursor(nullptr, IDC_WAIT);
452 SetCursor(hCur);
453 return TRUE;
456 LRESULT CCommitIsOnRefsDlg::OnGettingRefsFinished(WPARAM, LPARAM)
458 DialogEnableWindow(IDC_LOG, TRUE);
459 DialogEnableWindow(IDC_SELREF, TRUE);
460 DialogEnableWindow(IDC_FILTER, TRUE);
462 if (m_Rev.IsEmpty())
464 m_cRefList.ShowText(L"");
465 InvalidateRect(nullptr);
466 RefreshCursor();
467 return 0;
470 if (!m_gitrev.GetLastErr().IsEmpty())
472 CString msg;
473 msg.Format(IDS_PROC_REFINVALID, (LPCTSTR)m_Rev);
474 m_cRefList.ShowText(msg + _T("\n") + m_gitrev.GetLastErr());
476 InvalidateRect(nullptr);
477 RefreshCursor();
478 return 0;
481 SetDlgItemText(IDC_STATIC_SUBJECT, m_gitrev.m_CommitHash.ToString().Left(8) + L": " + m_gitrev.GetSubject());
482 if (!m_gitrev.m_CommitHash.IsEmpty())
483 m_tooltips.AddTool(IDC_STATIC_SUBJECT, CLoglistUtils::FormatDateAndTime(m_gitrev.GetAuthorDate(), DATE_SHORTDATE) + L" " + m_gitrev.GetAuthorName());
485 AddToList();
487 InvalidateRect(nullptr);
488 RefreshCursor();
489 return 0;
492 BOOL CCommitIsOnRefsDlg::PreTranslateMessage(MSG* pMsg)
494 if (pMsg->message == WM_KEYDOWN)
496 switch (pMsg->wParam)
498 case 'A':
500 if (GetFocus() != GetDlgItem(IDC_LIST_REF_LEAFS))
501 break;
502 if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
504 // select all entries
505 for (int i = 0; i < m_cRefList.GetItemCount(); ++i)
506 m_cRefList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
507 return TRUE;
510 break;
511 case 'C':
512 case VK_INSERT:
514 if (GetFocus() != GetDlgItem(IDC_LIST_REF_LEAFS))
515 break;
516 if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
518 CopySelectionToClipboard();
519 return TRUE;
522 break;
523 case VK_F5:
525 m_bRefsLoaded = false;
526 OnTimer(IDT_INPUT);
528 break;
529 case VK_ESCAPE:
530 if (GetFocus() == GetDlgItem(IDC_FILTER) && m_cFilter.GetWindowTextLength())
532 m_cFilter.SetWindowText(L"");
533 OnTimer(IDT_FILTER);
534 return TRUE;
536 break;
539 return __super::PreTranslateMessage(pMsg);
542 void CCommitIsOnRefsDlg::CopySelectionToClipboard()
544 // copy all selected paths to the clipboard
545 POSITION pos = m_cRefList.GetFirstSelectedItemPosition();
546 int index;
547 CString sTextForClipboard;
548 while ((index = m_cRefList.GetNextSelectedItem(pos)) >= 0)
550 sTextForClipboard += m_cRefList.GetItemText(index, 0);
551 sTextForClipboard += _T("\r\n");
553 CStringUtils::WriteAsciiStringToClipboard(sTextForClipboard);