1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2020, 2023 - 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.
22 #include "BrowseRefsDlg.h"
23 #include "MessageBox.h"
25 #include "StringUtils.h"
27 static UINT WM_GUIUPDATES
= RegisterWindowMessage(L
"TORTOISEGIT_CHOOSEVERSION_GUIUPDATES");
32 CString m_initialRefName
;
35 CWnd
* m_pWin
= nullptr;
36 CWinThread
* m_pLoadingThread
= nullptr;
37 static UINT
LoadingThreadEntry(LPVOID pVoid
)
39 return static_cast<CChooseVersion
*>(pVoid
)->LoadingThread();
41 volatile LONG m_bLoadingThreadRunning
= FALSE
;
44 CHistoryCombo m_ChooseVersioinBranch
;
45 CHistoryCombo m_ChooseVersioinTags
;
46 CHistoryCombo m_ChooseVersioinVersion
;
47 CButton m_RadioBranch
;
49 CString m_pendingRefName
;
50 bool m_bNotFullName
= true;
51 bool m_bSkipCurrentBranch
= false;
53 struct GUI_UPDATE_DATA
55 STRING_VECTOR branches
;
56 int current_branch_idx
= -1;
60 //Notification when version changed. Can be implemented in derived classes.
61 virtual void OnVersionChanged(){}
63 afx_msg
void OnBnClickedChooseRadio()
65 this->m_ChooseVersioinTags
.EnableWindow(FALSE
);
66 this->m_ChooseVersioinBranch
.EnableWindow(FALSE
);
67 this->m_ChooseVersioinVersion
.EnableWindow(FALSE
);
68 m_pWin
->GetDlgItem(IDC_BUTTON_BROWSE_REF
)->EnableWindow(FALSE
);
69 m_pWin
->GetDlgItem(IDC_BUTTON_SHOW
)->EnableWindow(FALSE
);
71 const int radio
= m_pWin
->GetCheckedRadioButton(IDC_RADIO_HEAD
, IDC_RADIO_VERSION
);
76 case IDC_RADIO_BRANCH
:
77 this->m_ChooseVersioinBranch
.EnableWindow(TRUE
);
78 m_pWin
->GetDlgItem(IDC_BUTTON_BROWSE_REF
)->EnableWindow(TRUE
);
82 this->m_ChooseVersioinTags
.EnableWindow(TRUE
);
84 case IDC_RADIO_VERSION
:
85 this->m_ChooseVersioinVersion
.EnableWindow(TRUE
);
86 m_pWin
->GetDlgItem(IDC_BUTTON_SHOW
)->EnableWindow(TRUE
);
89 // enable version browse button if Version is selected
90 m_pWin
->GetDlgItem(IDC_BUTTON_SHOW
)->EnableWindow(radio
== IDC_RADIO_VERSION
);
94 void OnBnClickedChooseVersion()
96 // use the git log to allow selection of a version
98 if (dlg
.IsThreadRunning())
100 CMessageBox::Show(m_pWin
->GetSafeHwnd(), IDS_PROC_LOG_ONLYONCE
, IDS_APPNAME
, MB_ICONEXCLAMATION
);
104 m_ChooseVersioinVersion
.GetWindowText(revision
);
105 dlg
.SetParams(CTGitPath(), CTGitPath(), revision
, revision
, 0);
106 // tell the dialog to use mode for selecting revisions
108 dlg
.ShowWorkingTreeChanges(false);
109 // only one revision must be selected however
110 dlg
.SingleSelection(true);
111 if (dlg
.DoModal() == IDOK
&& !dlg
.GetSelectedHash().empty())
113 m_ChooseVersioinVersion
.SetWindowText(dlg
.GetSelectedHash().at(0).ToString());
116 m_pWin
->BringWindowToTop(); /* cf. issue #3493 */
119 void UpdateRevsionName()
121 const int radio
= m_pWin
->GetCheckedRadioButton(IDC_RADIO_HEAD
, IDC_RADIO_VERSION
);
125 this->m_VersionName
= L
"HEAD";
127 case IDC_RADIO_BRANCH
:
128 this->m_VersionName
=m_ChooseVersioinBranch
.GetString();
129 if (!m_VersionName
.IsEmpty() && !g_Git
.IsBranchTagNameUnique(this->m_VersionName
))
130 this->m_VersionName
= L
"refs/heads/" + this->m_VersionName
;
133 this->m_VersionName
= m_ChooseVersioinTags
.GetString();
134 if (!m_VersionName
.IsEmpty() && !g_Git
.IsBranchTagNameUnique(this->m_VersionName
))
135 this->m_VersionName
= L
"refs/tags/" + m_ChooseVersioinTags
.GetString();
137 case IDC_RADIO_VERSION
:
138 this->m_VersionName
=m_ChooseVersioinVersion
.GetString();
142 void SetDefaultChoose(int id
)
144 m_pWin
->CheckRadioButton(IDC_RADIO_HEAD
,IDC_RADIO_VERSION
,id
);
145 OnBnClickedChooseRadio();
148 void OnBnClickedButtonBrowseRef()
152 CString resultRef
= CBrowseRefsDlg::PickRef(false, m_VersionName
, gPickRef_All
);
153 if(resultRef
.IsEmpty())
155 m_pendingRefName
= m_VersionName
;
156 m_bNotFullName
= true;
157 InitChooseVersion(false, true);
160 m_pendingRefName
= resultRef
;
161 m_bNotFullName
= false;
162 InitChooseVersion(false, true);
165 void SelectRef(CString refName
, bool bRefNameIsPossiblyNotFullName
= true)
167 if(bRefNameIsPossiblyNotFullName
)
169 //Make sure refName is a full ref name first
170 CString fullRefName
= g_Git
.GetFullRefName(refName
);
171 if(!fullRefName
.IsEmpty())
172 refName
= fullRefName
;
175 if (CStringUtils::StartsWith(refName
, L
"refs/"))
176 refName
= refName
.Mid(static_cast<int>(wcslen(L
"refs/")));
177 if (CStringUtils::StartsWith(refName
, L
"heads/"))
179 refName
= refName
.Mid(static_cast<int>(wcslen(L
"heads/")));
180 SetDefaultChoose(IDC_RADIO_BRANCH
);
181 m_ChooseVersioinBranch
.SetCurSel(
182 m_ChooseVersioinBranch
.FindStringExact(-1, refName
));
184 else if (CStringUtils::StartsWith(refName
, L
"remotes/"))
186 SetDefaultChoose(IDC_RADIO_BRANCH
);
187 m_ChooseVersioinBranch
.SetCurSel(
188 m_ChooseVersioinBranch
.FindStringExact(-1, refName
));
190 else if (CStringUtils::StartsWith(refName
, L
"tags/"))
192 refName
= refName
.Mid(static_cast<int>(wcslen(L
"refs/")));
193 if (CStringUtils::EndsWith(refName
, L
"^{}"))
194 refName
.Truncate(refName
.GetLength() - static_cast<int>(wcslen(L
"^{}")));
195 SetDefaultChoose(IDC_RADIO_TAGS
);
196 m_ChooseVersioinTags
.SetCurSel(
197 m_ChooseVersioinTags
.FindStringExact(-1, refName
));
201 SetDefaultChoose(IDC_RADIO_VERSION
);
202 m_ChooseVersioinVersion
.AddString(refName
);
209 GUI_UPDATE_DATA data
;
210 g_Git
.GetBranchList(data
.branches
, &data
.current_branch_idx
, CRegDWORD(L
"Software\\TortoiseGit\\BranchesIncludeFetchHead", TRUE
) ? CGit::BRANCH_ALL_F
: CGit::BRANCH_ALL
, m_bSkipCurrentBranch
);
212 g_Git
.GetTagList(data
.tags
);
214 m_pWin
->SendMessage(WM_GUIUPDATES
, reinterpret_cast<WPARAM
>(&data
));
216 InterlockedExchange(&m_bLoadingThreadRunning
, FALSE
);
219 void UpdateGUI(GUI_UPDATE_DATA
* data
= nullptr)
223 m_ChooseVersioinBranch
.SetList(data
->branches
);
224 m_ChooseVersioinBranch
.SetCurSel(data
->current_branch_idx
);
225 m_ChooseVersioinTags
.SetList(data
->tags
);
226 m_ChooseVersioinTags
.SetCurSel(0);
227 if (auto pCurrentBranch
= m_pWin
->GetDlgItem(IDC_CURRENTBRANCH
); pCurrentBranch
)
228 pCurrentBranch
->SetWindowText(g_Git
.GetCurrentBranch());
231 m_RadioBranch
.EnableWindow(TRUE
);
232 m_RadioTag
.EnableWindow(TRUE
);
234 if (m_pendingRefName
.IsEmpty())
237 SelectRef(m_pendingRefName
, m_bNotFullName
);
239 if (m_bIsFirstTimeToSetFocus
)
241 if (m_pWin
->GetDlgItem(IDC_COMBOBOXEX_BRANCH
)->IsWindowEnabled())
242 m_pWin
->GetDlgItem(IDC_COMBOBOXEX_BRANCH
)->SetFocus();
243 else if (m_pWin
->GetDlgItem(IDC_COMBOBOXEX_TAGS
)->IsWindowEnabled())
244 m_pWin
->GetDlgItem(IDC_COMBOBOXEX_TAGS
)->SetFocus();
245 else if (m_pWin
->GetDlgItem(IDC_COMBOBOXEX_VERSION
)->IsWindowEnabled())
246 m_pWin
->GetDlgItem(IDC_COMBOBOXEX_VERSION
)->SetFocus();
248 m_bIsFirstTimeToSetFocus
= false;
249 m_pWin
->GetDlgItem(IDOK
)->EnableWindow(TRUE
);
251 void InitChooseVersion(bool setFocusToBranchComboBox
= false, bool bReInit
= false)
253 m_ChooseVersioinBranch
.SetMaxHistoryItems(0x7FFFFFFF);
254 m_ChooseVersioinTags
.SetMaxHistoryItems(0x7FFFFFFF);
257 m_RadioBranch
.EnableWindow(FALSE
);
258 m_RadioTag
.EnableWindow(FALSE
);
260 m_bIsFirstTimeToSetFocus
= setFocusToBranchComboBox
;
263 m_pendingRefName
= m_initialRefName
;
264 m_bNotFullName
= true;
267 m_pWin
->GetDlgItem(IDOK
)->EnableWindow(FALSE
);
269 InterlockedExchange(&m_bLoadingThreadRunning
, TRUE
);
270 if ((m_pLoadingThread
= AfxBeginThread(LoadingThreadEntry
, this, 0, CREATE_SUSPENDED
)) == nullptr)
272 InterlockedExchange(&m_bLoadingThreadRunning
, FALSE
);
273 CMessageBox::Show(nullptr, IDS_ERR_THREADSTARTFAILED
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
277 m_pLoadingThread
->m_bAutoDelete
= FALSE
;
278 m_pLoadingThread
->ResumeThread();
280 void WaitForFinishLoading()
282 if(m_bLoadingThreadRunning
&& m_pLoadingThread
)
284 const DWORD ret
= ::WaitForSingleObject(m_pLoadingThread
->m_hThread
, 20000);
285 if(ret
== WAIT_TIMEOUT
)
286 ::TerminateThread(m_pLoadingThread
,0);
288 delete m_pLoadingThread
;
291 CString m_VersionName
;
292 bool m_bIsBranch
= false;
293 bool m_bIsFirstTimeToSetFocus
= false;
294 CChooseVersion(CWnd
*win
)
300 #define CHOOSE_VERSION_DDX \
301 DDX_Control(pDX, IDC_COMBOBOXEX_BRANCH, m_ChooseVersioinBranch); \
302 DDX_Control(pDX, IDC_COMBOBOXEX_TAGS, m_ChooseVersioinTags); \
303 DDX_Control(pDX, IDC_COMBOBOXEX_VERSION, m_ChooseVersioinVersion); \
304 DDX_Control(pDX, IDC_RADIO_BRANCH, m_RadioBranch);\
305 DDX_Control(pDX, IDC_RADIO_TAGS, m_RadioTag);
307 #define CHOOSE_VERSION_EVENT\
308 ON_REGISTERED_MESSAGE(WM_GUIUPDATES, OnUpdateGUIHost)\
309 ON_BN_CLICKED(IDC_RADIO_HEAD, OnBnClickedChooseRadioHost)\
310 ON_BN_CLICKED(IDC_RADIO_BRANCH, OnBnClickedChooseRadioHost)\
311 ON_BN_CLICKED(IDC_RADIO_TAGS, OnBnClickedChooseRadioHost)\
312 ON_BN_CLICKED(IDC_BUTTON_SHOW, OnBnClickedShow)\
313 ON_BN_CLICKED(IDC_RADIO_VERSION, OnBnClickedChooseRadioHost)\
314 ON_BN_CLICKED(IDC_BUTTON_BROWSE_REF, OnBnClickedButtonBrowseRefHost)
316 #define CHOOSE_VERSION_ADDANCHOR \
318 AddAnchor(IDC_COMBOBOXEX_BRANCH, TOP_LEFT, TOP_RIGHT); \
319 AddAnchor(IDC_COMBOBOXEX_TAGS, TOP_LEFT, TOP_RIGHT); \
320 AddAnchor(IDC_COMBOBOXEX_VERSION, TOP_LEFT, TOP_RIGHT); \
321 AddAnchor(IDC_GROUP_BASEON, TOP_LEFT, TOP_RIGHT); \
322 AddAnchor(IDC_BUTTON_SHOW,TOP_RIGHT); \
323 AddAnchor(IDC_BUTTON_BROWSE_REF,TOP_RIGHT); \
324 if (auto pCurrentBranch = GetDlgItem(IDC_CURRENTBRANCH); pCurrentBranch) \
325 AddAnchor(IDC_CURRENTBRANCH, TOP_LEFT, TOP_RIGHT); \
328 #define CHOOSE_EVENT_RADIO() \
329 LRESULT OnUpdateGUIHost(WPARAM data, LPARAM) { UpdateGUI(reinterpret_cast<GUI_UPDATE_DATA*>(data)); return 0; } \
330 afx_msg void OnBnClickedChooseRadioHost(){OnBnClickedChooseRadio();}\
331 afx_msg void OnBnClickedShow(){OnBnClickedChooseVersion();}\
332 afx_msg void OnBnClickedButtonBrowseRefHost(){OnBnClickedButtonBrowseRef();}