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.
20 #include "TortoiseProc.h"
22 #include "CommitIsOnRefsDlg.h"
23 #include "StringUtils.h"
24 #include "BrowseRefsDlg.h"
25 #include "RefLogDlg.h"
27 #include "LoglistUtils.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)
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
)
71 ON_REGISTERED_MESSAGE(WM_GETTINGREFSFINISHED
, OnGettingRefsFinished
)
74 // CCommitIsOnRefsDlg message handlers
76 void CCommitIsOnRefsDlg::OnCancel()
81 if (m_bNonModalParentHWND
)
90 void CCommitIsOnRefsDlg::PostNcDestroy()
92 if (m_bNonModalParentHWND
)
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
);
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
);
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);
135 m_cRevEdit
.SetWindowText(m_Rev
);
137 StartGetRefsThread();
139 m_cRevEdit
.SetFocus();
144 void CCommitIsOnRefsDlg::AddToList()
146 m_cRefList
.DeleteAllItems();
149 m_cFilter
.GetWindowText(filter
);
152 for (size_t i
= 0; i
< m_RefList
.size(); ++i
)
155 CString ref
= m_RefList
[i
];
156 if (CStringUtils::StartsWith(ref
, L
"refs/tags/"))
158 else if (CStringUtils::StartsWith(ref
, L
"refs/remotes/"))
160 else if (CStringUtils::StartsWith(ref
, L
"refs/heads/"))
163 if (ref
.Find(filter
) >= 0)
164 m_cRefList
.InsertItem(item
++, ref
, nImage
);
168 m_cRefList
.ShowText(L
"");
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
);
185 else if (nIDEvent
== IDT_INPUT
)
187 KillTimer(IDT_INPUT
);
188 StartGetRefsThread();
191 __super::OnTimer(nIDEvent
);
194 LRESULT
CCommitIsOnRefsDlg::OnClickedCancelFilter(WPARAM
/*wParam*/, LPARAM
/*lParam*/)
200 void CCommitIsOnRefsDlg::OnBnClickedShowLog()
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();
217 if (FillRevFromString(str
))
220 m_cRevEdit
.SetWindowText(str
);
224 if (entry
== 1) /*Log*/
228 m_cRevEdit
.GetWindowText(revision
);
229 dlg
.SetParams(CTGitPath(), CTGitPath(), revision
, revision
, 0);
231 if (dlg
.DoModal() == IDOK
)
233 if (dlg
.GetSelectedHash().empty())
236 if (FillRevFromString(dlg
.GetSelectedHash().at(0).ToString()))
239 m_cRevEdit
.SetWindowText(dlg
.GetSelectedHash().at(0).ToString());
245 if (entry
== 2) /*RefLog*/
248 if (dlg
.DoModal() == IDOK
)
250 if (FillRevFromString(dlg
.m_SelectedHash
))
253 m_cRevEdit
.SetWindowText(dlg
.m_SelectedHash
);
259 StartGetRefsThread();
260 KillTimer(IDT_INPUT
);
263 LRESULT
CCommitIsOnRefsDlg::OnEnChangeCommit(WPARAM
, LPARAM
)
265 SetTimer(IDT_INPUT
, 1000, nullptr);
269 void CCommitIsOnRefsDlg::OnContextMenu(CWnd
* pWnd
, CPoint point
)
271 if (!pWnd
|| pWnd
!= &m_cRefList
)
273 if (m_cRefList
.GetSelectedCount() == 0)
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))
280 m_cRefList
.GetItemRect(m_cRefList
.GetSelectionMark(), &rect
, LVIR_LABEL
);
281 m_cRefList
.ClientToScreen(&rect
);
282 point
= rect
.CenterPoint();
285 if (popup
.CreatePopupMenu())
287 STRING_VECTOR selectedRefs
;
288 POSITION pos
= m_cRefList
.GetFirstSelectedItemPosition();
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
);
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
);
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
);
311 popup
.AppendMenu(MF_SEPARATOR
);
312 popup
.AppendMenuIcon(eCmd_DiffWC
, IDS_LOG_POPUP_COMPARE
, IDI_DIFF
);
317 if (!selectedRefs
.empty())
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);
333 selectedRefs
[0] + L
"^{}",
334 selectedRefs
[1] + L
"^{}");
338 case eCmd_UnifiedDiff
:
339 CAppUtils::StartShowUnifiedDiff(GetSafeHwnd(), CTGitPath(), selectedRefs
.at(0), CTGitPath(), selectedRefs
.at(1), !!(GetAsyncKeyState(VK_SHIFT
) & 0x8000));
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
);
349 case eCmd_ViewLogRange
:
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
);
356 case eCmd_ViewLogRangeReachableFromOnlyOne
:
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
);
363 case eCmd_RepoBrowser
:
364 CAppUtils::RunTortoiseGitProc(L
"/command:repobrowser /path:\"" + g_Git
.m_CurrentDir
+ L
"\" /rev:" + selectedRefs
[0]);
367 CopySelectionToClipboard();
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
);
383 void CCommitIsOnRefsDlg::OnItemChangedListRefs(NMHDR
* pNMHDR
, LRESULT
* pResult
)
385 LPNMLISTVIEW pNMListView
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
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
)
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
);
411 return g_Git
.StripRefName(selectedRefs
.at(0)) + separator
+ g_Git
.StripRefName(lastSelected
);
414 void CCommitIsOnRefsDlg::StartGetRefsThread()
416 if (InterlockedExchange(&m_bThreadRunning
, TRUE
))
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
);
429 m_cRefList
.ShowText(CString(MAKEINTRESOURCE(IDS_STATUSLIST_BUSYMSG
)));
430 m_cRefList
.DeleteAllItems();
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()
451 m_cRevEdit
.RemoveSearchAll();
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
);
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
);
472 SendMessage(WM_GETTINGREFSFINISHED
);
474 InterlockedExchange(&m_bThreadRunning
, FALSE
);
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
);
486 return CResizableStandAloneDialog::OnSetCursor(pWnd
, nHitTest
, message
);
488 HCURSOR hCur
= LoadCursor(nullptr, IDC_WAIT
);
493 LRESULT
CCommitIsOnRefsDlg::OnGettingRefsFinished(WPARAM
, LPARAM
)
495 DialogEnableWindow(IDC_LOG
, TRUE
);
496 DialogEnableWindow(IDC_SELREF
, TRUE
);
497 DialogEnableWindow(IDC_FILTER
, TRUE
);
501 m_cRefList
.ShowText(L
"");
502 InvalidateRect(nullptr);
507 if (!m_gitrev
.GetLastErr().IsEmpty())
510 msg
.Format(IDS_PROC_REFINVALID
, (LPCTSTR
)m_Rev
);
511 m_cRefList
.ShowText(msg
+ L
'\n' + m_gitrev
.GetLastErr());
513 InvalidateRect(nullptr);
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());
524 InvalidateRect(nullptr);
529 BOOL
CCommitIsOnRefsDlg::PreTranslateMessage(MSG
* pMsg
)
531 if (pMsg
->message
== WM_KEYDOWN
)
533 switch (pMsg
->wParam
)
537 if (GetFocus() != GetDlgItem(IDC_LIST_REF_LEAFS
))
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
);
551 if (GetFocus() != GetDlgItem(IDC_LIST_REF_LEAFS
))
553 if (GetAsyncKeyState(VK_CONTROL
) & 0x8000)
555 CopySelectionToClipboard();
562 m_bRefsLoaded
= false;
567 if (GetFocus() == GetDlgItem(IDC_FILTER
) && m_cFilter
.GetWindowTextLength())
569 m_cFilter
.SetWindowText(L
"");
576 return __super::PreTranslateMessage(pMsg
);
579 void CCommitIsOnRefsDlg::CopySelectionToClipboard()
581 // copy all selected paths to the clipboard
582 POSITION pos
= m_cRefList
.GetFirstSelectedItemPosition();
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
);