Replace the custom messages defined with WM_USER with registered messages to avoid...
[TortoiseGit.git] / src / TortoiseProc / CommitIsOnRefsDlg.cpp
blobd900ed8f200ed4942adde87de32e943316ba791d
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2016-2017 - 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 "Git.h"
22 #include "CommitIsOnRefsDlg.h"
23 #include "StringUtils.h"
24 #include "BrowseRefsDlg.h"
25 #include "RefLogDlg.h"
26 #include "LogDlg.h"
27 #include "LoglistUtils.h"
28 #include "AppUtils.h"
29 #include "FileDiffDlg.h"
30 #include "MessageBox.h"
32 // CCommitIsOnRefsDlg dialog
34 UINT CCommitIsOnRefsDlg::WM_GETTINGREFSFINISHED = RegisterWindowMessage(L"TORTOISEGIT_CommitIsOnRefs_GETTINGREFSFINISHED");
36 IMPLEMENT_DYNAMIC(CCommitIsOnRefsDlg, CResizableStandAloneDialog)
38 CCommitIsOnRefsDlg::CCommitIsOnRefsDlg(CWnd* pParent /*=nullptr*/)
39 : CResizableStandAloneDialog(CCommitIsOnRefsDlg::IDD, pParent)
40 , m_bThreadRunning(FALSE)
41 , m_bRefsLoaded(false)
42 , m_bHasWC(true)
43 , m_bNonModalParentHWND(nullptr)
47 CCommitIsOnRefsDlg::~CCommitIsOnRefsDlg()
51 void CCommitIsOnRefsDlg::DoDataExchange(CDataExchange* pDX)
53 CDialog::DoDataExchange(pDX);
54 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_cRefList);
55 DDX_Control(pDX, IDC_FILTER, m_cFilter);
56 DDX_Control(pDX, IDC_COMMIT, m_cRevEdit);
57 DDX_Control(pDX, IDC_SELREF, m_cSelRevBtn);
60 BEGIN_MESSAGE_MAP(CCommitIsOnRefsDlg, CResizableStandAloneDialog)
61 ON_BN_CLICKED(IDC_SELREF, OnBnClickedSelRevBtn)
62 ON_BN_CLICKED(IDC_LOG, OnBnClickedShowLog)
63 ON_EN_CHANGE(IDC_FILTER, OnEnChangeEditFilter)
64 ON_REGISTERED_MESSAGE(CFilterEdit::WM_FILTEREDIT_CANCELCLICKED, OnClickedCancelFilter)
65 ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_REF_LEAFS, OnItemChangedListRefs)
66 ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, OnNMDblClickListRefs)
67 ON_MESSAGE(ENAC_UPDATE, OnEnChangeCommit)
68 ON_WM_TIMER()
69 ON_WM_CONTEXTMENU()
70 ON_WM_SETCURSOR()
71 ON_REGISTERED_MESSAGE(WM_GETTINGREFSFINISHED, OnGettingRefsFinished)
72 END_MESSAGE_MAP()
74 // CCommitIsOnRefsDlg message handlers
76 void CCommitIsOnRefsDlg::OnCancel()
78 if (m_bThreadRunning)
79 return;
81 if (m_bNonModalParentHWND)
83 DestroyWindow();
84 return;
87 __super::OnCancel();
90 void CCommitIsOnRefsDlg::PostNcDestroy()
92 if (m_bNonModalParentHWND)
93 delete this;
96 BOOL CCommitIsOnRefsDlg::OnInitDialog()
98 __super::OnInitDialog();
100 AddAnchor(IDC_FILTER, BOTTOM_LEFT, BOTTOM_RIGHT);
101 AddAnchor(IDC_LABEL_FILTER, BOTTOM_LEFT);
102 AddAnchor(IDC_SELREF, TOP_RIGHT);
103 AddAnchor(IDC_COMMIT, TOP_LEFT, TOP_RIGHT);
104 AddAnchor(IDC_STATIC_SUBJECT, TOP_LEFT, TOP_RIGHT);
105 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
106 AddAnchor(IDC_LOG, TOP_RIGHT);
107 AddOthersToAnchor();
109 m_bHasWC = !GitAdminDir::IsBareRepo(g_Git.m_CurrentDir);
111 m_cSelRevBtn.m_bRightArrow = TRUE;
112 m_cSelRevBtn.m_bDefaultClick = FALSE;
113 m_cSelRevBtn.m_bMarkDefault = FALSE;
114 m_cSelRevBtn.m_bShowCurrentItem = FALSE;
115 m_cSelRevBtn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFBROWSE)));
116 m_cSelRevBtn.AddEntry(CString(MAKEINTRESOURCE(IDS_LOG)));
117 m_cSelRevBtn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFLOG)));
119 EnableSaveRestore(L"CommitIsOnRefsDlg");
121 CImageList* imagelist = new CImageList();
122 imagelist->Create(IDB_BITMAP_REFTYPE, 16, 3, RGB(255, 255, 255));
123 m_cRefList.SetImageList(imagelist, LVSIL_SMALL);
125 CRect rect;
126 m_cRefList.GetClientRect(&rect);
127 m_cRefList.InsertColumn(0, L"Ref", 0, rect.Width() - 50);
128 if (CRegDWORD(L"Software\\TortoiseGit\\FullRowSelect", TRUE))
129 m_cRefList.SetExtendedStyle(m_cRefList.GetExtendedStyle() | LVS_EX_FULLROWSELECT);
131 m_cFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED, 14, 14);
132 m_cFilter.SetInfoIcon(IDI_LOGFILTER, 19, 19);
134 m_cRevEdit.Init();
135 m_cRevEdit.SetWindowText(m_Rev);
137 StartGetRefsThread();
139 m_cRevEdit.SetFocus();
141 return FALSE;
144 void CCommitIsOnRefsDlg::AddToList()
146 m_cRefList.DeleteAllItems();
148 CString filter;
149 m_cFilter.GetWindowText(filter);
151 int item = 0;
152 for (size_t i = 0; i < m_RefList.size(); ++i)
154 int nImage = -1;
155 CString ref = m_RefList[i];
156 if (CStringUtils::StartsWith(ref, L"refs/tags/"))
157 nImage = 0;
158 else if (CStringUtils::StartsWith(ref, L"refs/remotes/"))
159 nImage = 2;
160 else if (CStringUtils::StartsWith(ref, L"refs/heads/"))
161 nImage = 1;
163 if (ref.Find(filter) >= 0)
164 m_cRefList.InsertItem(item++, ref, nImage);
167 if (item)
168 m_cRefList.ShowText(L"");
169 else
170 m_cRefList.ShowText(CString(MAKEINTRESOURCE(IDS_ERROR_NOREF)));
173 void CCommitIsOnRefsDlg::OnEnChangeEditFilter()
175 SetTimer(IDT_FILTER, 1000, nullptr);
178 void CCommitIsOnRefsDlg::OnTimer(UINT_PTR nIDEvent)
180 if (nIDEvent == IDT_FILTER)
182 KillTimer(IDT_FILTER);
183 AddToList();
185 else if (nIDEvent == IDT_INPUT)
187 KillTimer(IDT_INPUT);
188 StartGetRefsThread();
191 __super::OnTimer(nIDEvent);
194 LRESULT CCommitIsOnRefsDlg::OnClickedCancelFilter(WPARAM /*wParam*/, LPARAM /*lParam*/)
196 OnTimer(IDT_FILTER);
197 return TRUE;
200 void CCommitIsOnRefsDlg::OnBnClickedShowLog()
202 CString cmd;
203 cmd.Format(L"/command:log /rev:%s", (LPCTSTR)m_gitrev.m_CommitHash.ToString());
204 CAppUtils::RunTortoiseGitProc(cmd);
207 void CCommitIsOnRefsDlg::OnBnClickedSelRevBtn()
209 INT_PTR entry = m_cSelRevBtn.GetCurrentEntry();
210 if (entry == 0) /* Browse Refence*/
213 CString str = CBrowseRefsDlg::PickRef();
214 if (str.IsEmpty())
215 return;
217 if (FillRevFromString(str))
218 return;
220 m_cRevEdit.SetWindowText(str);
224 if (entry == 1) /*Log*/
226 CLogDlg dlg;
227 CString revision;
228 m_cRevEdit.GetWindowText(revision);
229 dlg.SetParams(CTGitPath(), CTGitPath(), revision, revision, 0);
230 dlg.SetSelect(true);
231 if (dlg.DoModal() == IDOK)
233 if (dlg.GetSelectedHash().empty())
234 return;
236 if (FillRevFromString(dlg.GetSelectedHash().at(0).ToString()))
237 return;
239 m_cRevEdit.SetWindowText(dlg.GetSelectedHash().at(0).ToString());
241 else
242 return;
245 if (entry == 2) /*RefLog*/
247 CRefLogDlg dlg;
248 if (dlg.DoModal() == IDOK)
250 if (FillRevFromString(dlg.m_SelectedHash))
251 return;
253 m_cRevEdit.SetWindowText(dlg.m_SelectedHash);
255 else
256 return;
259 StartGetRefsThread();
260 KillTimer(IDT_INPUT);
263 LRESULT CCommitIsOnRefsDlg::OnEnChangeCommit(WPARAM, LPARAM)
265 SetTimer(IDT_INPUT, 1000, nullptr);
266 return 0;
269 void CCommitIsOnRefsDlg::OnContextMenu(CWnd* pWnd, CPoint point)
271 if (!pWnd || pWnd != &m_cRefList)
272 return;
273 if (m_cRefList.GetSelectedCount() == 0)
274 return;
275 // if the context menu is invoked through the keyboard, we have to use
276 // a calculated position on where to anchor the menu on
277 if ((point.x == -1) && (point.y == -1))
279 CRect rect;
280 m_cRefList.GetItemRect(m_cRefList.GetSelectionMark(), &rect, LVIR_LABEL);
281 m_cRefList.ClientToScreen(&rect);
282 point = rect.CenterPoint();
284 CIconMenu popup;
285 if (popup.CreatePopupMenu())
287 STRING_VECTOR selectedRefs;
288 POSITION pos = m_cRefList.GetFirstSelectedItemPosition();
289 while (pos)
290 selectedRefs.push_back(m_cRefList.GetItemText(m_cRefList.GetNextSelectedItem(pos), 0));
291 bool needSep = false;
292 if (selectedRefs.size() == 2)
294 popup.AppendMenuIcon(eCmd_Diff, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF);
295 popup.AppendMenuIcon(eCmd_UnifiedDiff, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);
296 popup.AppendMenu(MF_SEPARATOR, NULL);
297 CString menu;
298 menu.Format(IDS_SHOWLOG_OF, (LPCTSTR)GetTwoSelectedRefs(selectedRefs, m_sLastSelected, L".."));
299 popup.AppendMenuIcon(eCmd_ViewLogRange, menu, IDI_LOG);
300 menu.Format(IDS_SHOWLOG_OF, (LPCTSTR)GetTwoSelectedRefs(selectedRefs, m_sLastSelected, L"..."));
301 popup.AppendMenuIcon(eCmd_ViewLogRangeReachableFromOnlyOne, menu, IDI_LOG);
302 needSep = true;
304 else if (selectedRefs.size() == 1)
306 popup.AppendMenuIcon(eCmd_ViewLog, IDS_FILEDIFF_LOG, IDI_LOG);
307 popup.AppendMenu(MF_SEPARATOR, NULL);
308 popup.AppendMenuIcon(eCmd_RepoBrowser, IDS_LOG_BROWSEREPO, IDI_REPOBROWSE);
309 if (m_bHasWC)
311 popup.AppendMenu(MF_SEPARATOR);
312 popup.AppendMenuIcon(eCmd_DiffWC, IDS_LOG_POPUP_COMPARE, IDI_DIFF);
314 needSep = true;
317 if (!selectedRefs.empty())
319 if (needSep)
320 popup.AppendMenu(MF_SEPARATOR, NULL);
322 popup.AppendMenuIcon(eCmd_Copy, IDS_SCIEDIT_COPY, IDI_COPYCLIP);
325 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this);
326 switch (cmd)
328 case eCmd_Diff:
330 CFileDiffDlg dlg;
331 dlg.SetDiff(
332 nullptr,
333 selectedRefs[0] + L"^{}",
334 selectedRefs[1] + L"^{}");
335 dlg.DoModal();
337 break;
338 case eCmd_UnifiedDiff:
339 CAppUtils::StartShowUnifiedDiff(GetSafeHwnd(), CTGitPath(), selectedRefs.at(0), CTGitPath(), selectedRefs.at(1), !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
340 break;
341 case eCmd_ViewLog:
343 CString sCmd = L"/command:log";
344 sCmd += L" /path:\"" + g_Git.m_CurrentDir + L"\" ";
345 sCmd += L" /endrev:" + selectedRefs.at(0);
346 CAppUtils::RunTortoiseGitProc(sCmd);
348 break;
349 case eCmd_ViewLogRange:
351 CString sCmd;
352 sCmd.Format(L"/command:log /path:\"%s\" /range:\"%s\"", (LPCTSTR)g_Git.m_CurrentDir, (LPCTSTR)GetTwoSelectedRefs(selectedRefs, m_sLastSelected, L".."));
353 CAppUtils::RunTortoiseGitProc(sCmd);
355 break;
356 case eCmd_ViewLogRangeReachableFromOnlyOne:
358 CString sCmd;
359 sCmd.Format(L"/command:log /path:\"%s\" /range:\"%s\"", (LPCTSTR)g_Git.m_CurrentDir, (LPCTSTR)GetTwoSelectedRefs(selectedRefs, m_sLastSelected, L"..."));
360 CAppUtils::RunTortoiseGitProc(sCmd);
362 break;
363 case eCmd_RepoBrowser:
364 CAppUtils::RunTortoiseGitProc(L"/command:repobrowser /path:\"" + g_Git.m_CurrentDir + L"\" /rev:" + selectedRefs[0]);
365 break;
366 case eCmd_Copy:
367 CopySelectionToClipboard();
368 break;
369 case eCmd_DiffWC:
371 CString sCmd;
372 sCmd.Format(L"/command:showcompare /path:\"%s\" /revision1:%s /revision2:%s", (LPCTSTR)g_Git.m_CurrentDir, (LPCTSTR)selectedRefs[0], (LPCTSTR)GitRev::GetWorkingCopy());
373 if (!!(GetAsyncKeyState(VK_SHIFT) & 0x8000))
374 sCmd += L" /alternative";
376 CAppUtils::RunTortoiseGitProc(sCmd);
378 break;
383 void CCommitIsOnRefsDlg::OnItemChangedListRefs(NMHDR* pNMHDR, LRESULT* pResult)
385 LPNMLISTVIEW pNMListView = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
386 *pResult = 0;
388 if (pNMListView->iItem >= 0 && m_RefList.size() > (size_t)pNMListView->iItem && (pNMListView->uNewState & LVIS_SELECTED))
389 m_sLastSelected = m_RefList[pNMListView->iItem];
392 void CCommitIsOnRefsDlg::OnNMDblClickListRefs(NMHDR* pNMHDR, LRESULT* pResult)
394 *pResult = 0;
396 LPNMLISTVIEW pNMListView = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
397 if (m_bNonModalParentHWND && pNMListView->iItem >= 0 && m_RefList.size() > (size_t)pNMListView->iItem)
399 if (::SendMessage(m_bNonModalParentHWND, CGitLogListBase::m_ScrollToRef, (WPARAM)&m_RefList[pNMListView->iItem], 0) != 0)
400 FlashWindowEx(FLASHW_ALL, 2, 100);
404 CString CCommitIsOnRefsDlg::GetTwoSelectedRefs(const STRING_VECTOR& selectedRefs, const CString& lastSelected, const CString& separator)
406 ASSERT(selectedRefs.size() == 2);
408 if (selectedRefs.at(0) == lastSelected)
409 return g_Git.StripRefName(selectedRefs.at(1)) + separator + g_Git.StripRefName(lastSelected);
410 else
411 return g_Git.StripRefName(selectedRefs.at(0)) + separator + g_Git.StripRefName(lastSelected);
414 void CCommitIsOnRefsDlg::StartGetRefsThread()
416 if (InterlockedExchange(&m_bThreadRunning, TRUE))
417 return;
419 KillTimer(IDT_INPUT);
421 SetDlgItemText(IDC_STATIC_SUBJECT, L"");
422 m_tooltips.DelTool(IDC_STATIC_SUBJECT);
424 DialogEnableWindow(IDC_LOG, FALSE);
425 DialogEnableWindow(IDC_SELREF, FALSE);
426 DialogEnableWindow(IDC_FILTER, FALSE);
428 m_RefList.clear();
429 m_cRefList.ShowText(CString(MAKEINTRESOURCE(IDS_STATUSLIST_BUSYMSG)));
430 m_cRefList.DeleteAllItems();
431 RefreshCursor();
433 m_cRevEdit.GetWindowText(m_Rev);
435 if (!AfxBeginThread(GetRefsThreadEntry, this))
437 InterlockedExchange(&m_bThreadRunning, FALSE);
438 CMessageBox::Show(GetSafeHwnd(), IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
442 UINT CCommitIsOnRefsDlg::GetRefsThreadEntry(LPVOID pVoid)
444 return reinterpret_cast<CCommitIsOnRefsDlg*>(pVoid)->GetRefsThread();
447 UINT CCommitIsOnRefsDlg::GetRefsThread()
449 if (!m_bRefsLoaded)
451 m_cRevEdit.RemoveSearchAll();
452 STRING_VECTOR refs;
453 g_Git.GetRefList(refs);
454 for (const auto& ref : refs)
455 m_cRevEdit.AddSearchString(ref);
456 m_bRefsLoaded = true;
459 if (m_Rev.IsEmpty() || m_gitrev.GetCommit(m_Rev))
461 SendMessage(WM_GETTINGREFSFINISHED);
463 InterlockedExchange(&m_bThreadRunning, FALSE);
464 return 0;
467 if (g_Git.GetRefsCommitIsOn(m_RefList, m_gitrev.m_CommitHash, true, true, CGit::BRANCH_ALL))
469 MessageBox(g_Git.GetGitLastErr(L"Could not get all refs."), L"TortoiseGit", MB_ICONERROR);
470 return 0;
472 SendMessage(WM_GETTINGREFSFINISHED);
474 InterlockedExchange(&m_bThreadRunning, FALSE);
475 return 0;
478 BOOL CCommitIsOnRefsDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
480 if (pWnd != &m_cRefList)
481 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
482 if (!m_bThreadRunning)
484 HCURSOR hCur = LoadCursor(nullptr, IDC_ARROW);
485 SetCursor(hCur);
486 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
488 HCURSOR hCur = LoadCursor(nullptr, IDC_WAIT);
489 SetCursor(hCur);
490 return TRUE;
493 LRESULT CCommitIsOnRefsDlg::OnGettingRefsFinished(WPARAM, LPARAM)
495 DialogEnableWindow(IDC_LOG, TRUE);
496 DialogEnableWindow(IDC_SELREF, TRUE);
497 DialogEnableWindow(IDC_FILTER, TRUE);
499 if (m_Rev.IsEmpty())
501 m_cRefList.ShowText(L"");
502 InvalidateRect(nullptr);
503 RefreshCursor();
504 return 0;
507 if (!m_gitrev.GetLastErr().IsEmpty())
509 CString msg;
510 msg.Format(IDS_PROC_REFINVALID, (LPCTSTR)m_Rev);
511 m_cRefList.ShowText(msg + L'\n' + m_gitrev.GetLastErr());
513 InvalidateRect(nullptr);
514 RefreshCursor();
515 return 0;
518 SetDlgItemText(IDC_STATIC_SUBJECT, m_gitrev.m_CommitHash.ToString().Left(8) + L": " + m_gitrev.GetSubject());
519 if (!m_gitrev.m_CommitHash.IsEmpty())
520 m_tooltips.AddTool(IDC_STATIC_SUBJECT, CLoglistUtils::FormatDateAndTime(m_gitrev.GetAuthorDate(), DATE_SHORTDATE) + L" " + m_gitrev.GetAuthorName());
522 AddToList();
524 InvalidateRect(nullptr);
525 RefreshCursor();
526 return 0;
529 BOOL CCommitIsOnRefsDlg::PreTranslateMessage(MSG* pMsg)
531 if (pMsg->message == WM_KEYDOWN)
533 switch (pMsg->wParam)
535 case 'A':
537 if (GetFocus() != GetDlgItem(IDC_LIST_REF_LEAFS))
538 break;
539 if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
541 // select all entries
542 for (int i = 0; i < m_cRefList.GetItemCount(); ++i)
543 m_cRefList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
544 return TRUE;
547 break;
548 case 'C':
549 case VK_INSERT:
551 if (GetFocus() != GetDlgItem(IDC_LIST_REF_LEAFS))
552 break;
553 if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
555 CopySelectionToClipboard();
556 return TRUE;
559 break;
560 case VK_F5:
562 m_bRefsLoaded = false;
563 OnTimer(IDT_INPUT);
565 break;
566 case VK_ESCAPE:
567 if (GetFocus() == GetDlgItem(IDC_FILTER) && m_cFilter.GetWindowTextLength())
569 m_cFilter.SetWindowText(L"");
570 OnTimer(IDT_FILTER);
571 return TRUE;
573 break;
576 return __super::PreTranslateMessage(pMsg);
579 void CCommitIsOnRefsDlg::CopySelectionToClipboard()
581 // copy all selected paths to the clipboard
582 POSITION pos = m_cRefList.GetFirstSelectedItemPosition();
583 int index;
584 CString sTextForClipboard;
585 while ((index = m_cRefList.GetNextSelectedItem(pos)) >= 0)
587 sTextForClipboard += m_cRefList.GetItemText(index, 0);
588 sTextForClipboard += L"\r\n";
590 CStringUtils::WriteAsciiStringToClipboard(sTextForClipboard);