Force parameter is never used
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blob2fda6a33eed74b1935d6ac4bb9e559ceabc77eac
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2014 - 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 // BrowseRefsDlg.cpp : implementation file
22 #include "stdafx.h"
23 #include "TortoiseProc.h"
24 #include "BrowseRefsDlg.h"
25 #include "LogDlg.h"
26 #include "AddRemoteDlg.h"
27 #include "AppUtils.h"
28 #include "Settings\SettingGitRemote.h"
29 #include "SinglePropSheetDlg.h"
30 #include "MessageBox.h"
31 #include "RefLogDlg.h"
32 #include "IconMenu.h"
33 #include "FileDiffDlg.h"
34 #include "DeleteRemoteTagDlg.h"
35 #include "UnicodeUtils.h"
36 #include "InputDlg.h"
37 #include "SysProgressDlg.h"
39 static int SplitRemoteBranchName(CString ref, CString &remote, CString &branch)
41 if (ref.Left(13) == _T("refs/remotes/"))
42 ref = ref.Mid(13);
43 else if (ref.Left(8) == _T("remotes/"))
44 ref = ref.Mid(8);
46 STRING_VECTOR list;
47 int result = g_Git.GetRemoteList(list);
48 if (result != 0)
49 return result;
51 for (size_t i = 0; i < list.size(); ++i)
53 if (ref.Left(list[i].GetLength() + 1) == list[i] + _T("/"))
55 remote = list[i];
56 branch = ref.Mid(list[i].GetLength() + 1);
57 return 0;
59 if (ref == list[i])
61 remote = list[i];
62 branch = _T("");
63 return 0;
67 return -1;
70 void SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)
72 if (control == NULL)
73 return;
74 // set the sort arrow
75 CHeaderCtrl * pHeader = control->GetHeaderCtrl();
76 HDITEM HeaderItem = {0};
77 HeaderItem.mask = HDI_FORMAT;
78 for (int i=0; i<pHeader->GetItemCount(); ++i)
80 pHeader->GetItem(i, &HeaderItem);
81 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
82 pHeader->SetItem(i, &HeaderItem);
84 if (nColumn >= 0)
86 pHeader->GetItem(nColumn, &HeaderItem);
87 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);
88 pHeader->SetItem(nColumn, &HeaderItem);
92 class CRefLeafListCompareFunc
94 public:
95 CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){
96 m_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER);
97 if (m_bSortLogical)
98 m_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE);
101 static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
103 return ((CRefLeafListCompareFunc*)lParamSort)->Compare(lParam1,lParam2);
106 int Compare(LPARAM lParam1, LPARAM lParam2)
108 return Compare(
109 (CShadowTree*)m_pList->GetItemData((int)lParam1),
110 (CShadowTree*)m_pList->GetItemData((int)lParam2));
113 int Compare(const CShadowTree* pLeft, const CShadowTree* pRight)
115 int result=CompareNoDesc(pLeft,pRight);
116 if(m_desc)
117 return -result;
118 return result;
121 int CompareNoDesc(const CShadowTree* pLeft, const CShadowTree* pRight)
123 switch(m_col)
125 case CBrowseRefsDlg::eCol_Name: return SortStrCmp(pLeft->GetRefName(), pRight->GetRefName());
126 case CBrowseRefsDlg::eCol_Upstream: return SortStrCmp(pLeft->m_csUpstream, pRight->m_csUpstream);
127 case CBrowseRefsDlg::eCol_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);
128 case CBrowseRefsDlg::eCol_Msg: return SortStrCmp(pLeft->m_csSubject, pRight->m_csSubject);
129 case CBrowseRefsDlg::eCol_LastAuthor: return SortStrCmp(pLeft->m_csAuthor, pRight->m_csAuthor);
130 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);
131 case CBrowseRefsDlg::eCol_Description: return SortStrCmp(pLeft->m_csDescription, pRight->m_csDescription);
133 return 0;
135 int SortStrCmp(const CString& left, const CString& right)
137 if (m_bSortLogical)
138 return StrCmpLogicalW(left, right);
139 return StrCmpI(left, right);
142 int m_col;
143 bool m_desc;
144 CListCtrl* m_pList;
145 bool m_bSortLogical;
148 // CBrowseRefsDlg dialog
150 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)
152 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)
153 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),
154 m_cmdPath(cmdPath),
155 m_currSortCol(0),
156 m_currSortDesc(false),
157 m_initialRef(L"HEAD"),
158 m_pickRef_Kind(gPickRef_All),
159 m_pListCtrlRoot(NULL),
160 m_bHasWC(true),
161 m_SelectedFilters(LOGFILTER_ALL),
162 m_ColumnManager(&m_ListRefLeafs),
163 m_bPickOne(false),
164 m_bPickedRefSet(false)
169 CBrowseRefsDlg::~CBrowseRefsDlg()
173 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)
175 CDialog::DoDataExchange(pDX);
176 DDX_Control(pDX, IDC_TREE_REF, m_RefTreeCtrl);
177 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_ListRefLeafs);
178 DDX_Control(pDX, IDC_BROWSEREFS_EDIT_FILTER, m_ctrlFilter);
182 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)
183 ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)
184 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)
185 ON_WM_CONTEXTMENU()
186 ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)
187 ON_WM_DESTROY()
188 ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)
189 ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnItemChangedListRefLeafs)
190 ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs)
191 ON_NOTIFY(LVN_BEGINLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs)
192 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER, &CBrowseRefsDlg::OnEnChangeEditFilter)
193 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED, OnClickedInfoIcon)
194 ON_WM_TIMER()
195 ON_BN_CLICKED(IDC_CURRENTBRANCH, OnBnClickedCurrentbranch)
196 END_MESSAGE_MAP()
199 // CBrowseRefsDlg message handlers
201 void CBrowseRefsDlg::OnBnClickedOk()
203 if (m_bPickOne || m_ListRefLeafs.GetSelectedCount() != 2)
205 OnOK();
206 return;
209 CIconMenu popupMenu;
210 popupMenu.CreatePopupMenu();
212 std::vector<CShadowTree*> selectedLeafs;
213 GetSelectedLeaves(selectedLeafs);
215 popupMenu.AppendMenuIcon(1, GetSelectedRef(true, false), IDI_LOG);
216 popupMenu.SetDefaultItem(1);
217 popupMenu.AppendMenuIcon(2, GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("..")), IDI_LOG);
218 popupMenu.AppendMenuIcon(3, GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("...")), IDI_LOG);
220 RECT rect;
221 GetDlgItem(IDOK)->GetWindowRect(&rect);
222 int selection = popupMenu.TrackPopupMenuEx(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, rect.left, rect.top, this, 0);
223 switch (selection)
225 case 1:
226 OnOK();
227 break;
228 case 2:
230 m_bPickedRefSet = true;
231 m_pickedRef = GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T(".."));
232 OnOK();
234 break;
235 case 3:
237 m_bPickedRefSet = true;
238 m_pickedRef = GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("..."));
239 OnOK();
241 break;
242 default:
243 break;
247 BOOL CBrowseRefsDlg::OnInitDialog()
249 CResizableStandAloneDialog::OnInitDialog();
250 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
252 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
253 m_ctrlFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
254 m_ctrlFilter.SetInfoIcon(IDI_FILTEREDIT);
255 SetFilterCueText();
257 AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);
258 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
259 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER, TOP_LEFT);
260 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER, TOP_LEFT, TOP_RIGHT);
261 AddAnchor(IDC_INFOLABEL, BOTTOM_LEFT, BOTTOM_RIGHT);
262 AddAnchor(IDHELP, BOTTOM_RIGHT);
264 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
265 static UINT columnNames[] = { IDS_BRANCHNAME, IDS_TRACKEDBRANCH, IDS_DATELASTCOMMIT, IDS_LASTCOMMIT, IDS_LASTAUTHOR, IDS_HASH, IDS_DESCRIPTION };
266 static int columnWidths[] = { 150, 100, 100, 300, 100, 80, 80 };
267 DWORD dwDefaultColumns = (1 << eCol_Name) | (1 << eCol_Upstream ) | (1 << eCol_Date) | (1 << eCol_Msg) |
268 (1 << eCol_LastAuthor) | (1 << eCol_Hash) | (1 << eCol_Description);
269 m_ColumnManager.SetNames(columnNames, _countof(columnNames));
270 m_ColumnManager.ReadSettings(dwDefaultColumns, 0, _T("BrowseRefs"), _countof(columnNames), columnWidths);
271 m_bPickedRefSet = false;
273 AddAnchor(IDOK,BOTTOM_RIGHT);
274 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
275 AddAnchor(IDC_CURRENTBRANCH, BOTTOM_RIGHT);
277 Refresh(m_initialRef);
279 EnableSaveRestore(L"BrowseRefs");
281 CString sWindowTitle;
282 GetWindowText(sWindowTitle);
283 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
285 m_bHasWC = !g_GitAdminDir.IsBareRepo(g_Git.m_CurrentDir);
287 if (m_bPickOne)
288 m_ListRefLeafs.ModifyStyle(0, LVS_SINGLESEL);
290 m_ListRefLeafs.SetFocus();
291 return FALSE;
294 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
296 int posSlash=nameLeft.Find('/');
297 CString nameSub;
298 if(posSlash<0)
300 nameSub=nameLeft;
301 nameLeft.Empty();//Nothing left
303 else
305 nameSub=nameLeft.Left(posSlash);
306 nameLeft=nameLeft.Mid(posSlash+1);
308 if(nameSub.IsEmpty())
309 return NULL;
311 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
312 return NULL;
314 CShadowTree& nextNode=m_ShadowTree[nameSub];
315 nextNode.m_csRefName=nameSub;
316 nextNode.m_pParent=this;
317 return &nextNode;
320 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)
322 if(IsLeaf())
324 if(m_csRefName.GetLength() > partialRefName.GetLength())
325 return NULL;
326 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)
328 //Match of leaf name. Try match on total name.
329 CString totalRefName = GetRefName();
330 if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)
331 return this; //Also match. Found.
334 else
336 //Not a leaf. Search all nodes.
337 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)
339 CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);
340 if(pSubtree != NULL)
341 return pSubtree; //Found
344 return NULL;//Not found
348 typedef std::map<CString,CString> MAP_STRING_STRING;
350 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf, bool pickFirstSelIfMultiSel)
352 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
353 //List ctrl selection?
354 if(pos && (pickFirstSelIfMultiSel || m_ListRefLeafs.GetSelectedCount() == 1))
356 //A leaf is selected
357 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(
358 m_ListRefLeafs.GetNextSelectedItem(pos));
359 return pTree->GetRefName();
361 else if (pos && !pickFirstSelIfMultiSel)
363 // at least one leaf is selected
364 CString refs;
365 int index;
366 while ((index = m_ListRefLeafs.GetNextSelectedItem(pos)) >= 0)
368 CString ref = ((CShadowTree*)m_ListRefLeafs.GetItemData(index))->GetRefName();
369 if(wcsncmp(ref, L"refs/", 5) == 0)
370 ref = ref.Mid(5);
371 if(wcsncmp(ref, L"heads/", 6) == 0)
372 ref = ref.Mid(6);
373 refs += ref + _T(" ");
375 return refs.Trim();
377 else if(!onlyIfLeaf)
379 //Tree ctrl selection?
380 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
381 if(hTree!=NULL)
383 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);
384 return pTree->GetRefName();
387 return CString();//None
390 static int GetBranchDescriptionsCallback(const git_config_entry *entry, void *data)
392 MAP_STRING_STRING *descriptions = (MAP_STRING_STRING *) data;
393 CString key = CUnicodeUtils::GetUnicode(entry->name, CP_UTF8);
394 CString val = CUnicodeUtils::GetUnicode(entry->value, CP_UTF8);
395 descriptions->insert(std::make_pair(key.Mid(7, key.GetLength() - 7 - 12), val)); // 7: branch., 12: .description
396 return 0;
399 MAP_STRING_STRING GetBranchDescriptions()
401 MAP_STRING_STRING descriptions;
402 git_config * config;
403 git_config_new(&config);
404 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL, FALSE);
405 git_config_foreach_match(config, "branch\\..*\\.description", GetBranchDescriptionsCallback, &descriptions);
406 git_config_free(config);
407 return descriptions;
410 void CBrowseRefsDlg::Refresh(CString selectRef)
412 // m_RefMap.clear();
413 // g_Git.GetMapHashToFriendName(m_RefMap);
415 if(!selectRef.IsEmpty())
417 if(selectRef == "HEAD")
419 selectRef = g_Git.GetSymbolicRef(selectRef, false);
422 else
424 selectRef = GetSelectedRef(false, true);
427 m_RefTreeCtrl.DeleteAllItems();
428 m_ListRefLeafs.DeleteAllItems();
429 m_TreeRoot.m_ShadowTree.clear();
430 m_TreeRoot.m_csRefName = "refs";
431 m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"refs",NULL,NULL);
432 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);
434 CString allRefs, error;
435 if (g_Git.Run(L"git.exe for-each-ref --format="
436 L"%(refname)%04"
437 L"%(objectname)%04"
438 L"%(upstream)%04"
439 L"%(authordate:relative)%04"
440 L"%(subject)%04"
441 L"%(authorname)%04"
442 L"%(authordate:iso8601)%03",
443 &allRefs, &error, CP_UTF8))
445 CMessageBox::Show(NULL, CString(_T("Get refs failed\n")) + error, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
448 int linePos=0;
449 CString singleRef;
451 MAP_STRING_STRING refMap;
453 //First sort on ref name
454 while(!(singleRef=allRefs.Tokenize(L"\03",linePos)).IsEmpty())
456 singleRef.TrimLeft(L"\r\n");
457 int valuePos=0;
458 CString refName=singleRef.Tokenize(L"\04",valuePos);
459 if(refName.IsEmpty())
460 continue;
461 CString refRest=singleRef.Mid(valuePos);
464 //Use ref based on m_pickRef_Kind
465 if (wcsncmp(refName, L"refs/heads/", 11) == 0 && !(m_pickRef_Kind & gPickRef_Head))
466 continue; //Skip
467 if (wcsncmp(refName, L"refs/tags/", 10) == 0 && !(m_pickRef_Kind & gPickRef_Tag))
468 continue; //Skip
469 if (wcsncmp(refName, L"refs/remotes/", 13) == 0 && !(m_pickRef_Kind & gPickRef_Remote))
470 continue; //Skip
472 refMap[refName] = refRest; //Use
475 MAP_STRING_STRING descriptions = GetBranchDescriptions();
477 //Populate ref tree
478 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
480 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
481 CString values=iterRefMap->second;
482 values.Replace(L"\04" L"\04",L"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
484 int valuePos=0;
485 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
486 treeLeaf.m_csUpstream = values.Tokenize(L"\04", valuePos); if (valuePos < 0) continue;
487 CGit::GetShortName(treeLeaf.m_csUpstream, treeLeaf.m_csUpstream, L"refs/remotes/");
488 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
489 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
490 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
491 treeLeaf.m_csDate_Iso8601= values.Tokenize(L"\04",valuePos);
493 if (wcsncmp(iterRefMap->first, L"refs/heads/", 11) == 0)
494 treeLeaf.m_csDescription = descriptions[treeLeaf.m_csRefName];
497 // always expand the tree first
498 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree, TVE_EXPAND);
500 // try exact match first
501 if (!selectRef.IsEmpty() && !SelectRef(selectRef, true))
502 SelectRef(selectRef, false);
505 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
507 if(!bExactMatch)
509 CString newRefName = GetFullRefName(refName);
510 if(!newRefName.IsEmpty())
511 refName = newRefName;
512 //else refName is not a valid ref. Try to select as good as possible.
514 if(_wcsnicmp(refName, L"refs/", 5) != 0)
515 return false; // Not a ref name
517 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
518 if(treeLeafHead.m_hTree != NULL)
520 //Not a leaf. Select tree node and return
521 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
522 return true;
525 if(treeLeafHead.m_pParent==NULL)
526 return false; //Weird... should not occur.
528 //This is the current head.
529 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
531 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
533 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
534 if(pCurrShadowTree == &treeLeafHead)
536 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
537 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
541 return true;
544 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
546 if(pTreePos==NULL)
548 if(_wcsnicmp(refName, L"refs/", 5) == 0)
549 refName=refName.Mid(5);
550 pTreePos=&m_TreeRoot;
552 if(refName.IsEmpty())
553 return *pTreePos;//Found leaf
555 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
556 if(pNextTree==NULL)
558 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
559 ASSERT(!bCreateIfNotExist);
560 return *pTreePos;
563 if(!refName.IsEmpty())
565 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
566 //Leafs are for the list control.
567 if(pNextTree->m_hTree==NULL)
569 //New tree. Create node in control.
570 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
571 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
575 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
579 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
581 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
582 *pResult = 0;
584 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
587 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
589 m_ListRefLeafs.DeleteAllItems();
591 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
592 if(pTree==NULL)
594 ASSERT(FALSE);
595 return;
597 FillListCtrlForShadowTree(pTree,L"",true);
598 m_ColumnManager.SetVisible(eCol_Upstream, (wcsncmp(pTree->GetRefName(), L"refs/heads", 11) == 0));
601 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
603 if(pTree->IsLeaf())
605 CString filter;
606 m_ctrlFilter.GetWindowText(filter);
607 filter.MakeLower();
608 CString ref = refNamePrefix + pTree->m_csRefName;
609 if (!(pTree->m_csRefName.IsEmpty() || pTree->m_csRefName == "refs" && pTree->m_pParent == NULL) && IsMatchFilter(pTree, ref, filter))
611 int indexItem = m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(), L"");
613 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
614 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, ref);
615 m_ListRefLeafs.SetItemText(indexItem, eCol_Upstream, pTree->m_csUpstream);
616 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);
617 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
618 m_ListRefLeafs.SetItemText(indexItem,eCol_LastAuthor, pTree->m_csAuthor);
619 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
620 int pos = 0;
621 m_ListRefLeafs.SetItemText(indexItem,eCol_Description, pTree->m_csDescription.Tokenize(_T("\n"), pos));
624 else
627 CString csThisName;
628 if(!isFirstLevel)
629 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
630 else
631 m_pListCtrlRoot = pTree;
632 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
634 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
637 if (isFirstLevel)
639 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
640 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
642 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
646 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree* pTree, const CString &ref, const CString &filter)
648 if (m_SelectedFilters & LOGFILTER_REFNAME)
650 CString msg = ref;
651 msg = msg.MakeLower();
653 if (msg.Find(filter) >= 0)
654 return true;
657 if (m_SelectedFilters & LOGFILTER_SUBJECT)
659 CString msg = pTree->m_csSubject;
660 msg = msg.MakeLower();
662 if (msg.Find(filter) >= 0)
663 return true;
666 if (m_SelectedFilters & LOGFILTER_AUTHORS)
668 CString msg = pTree->m_csAuthor;
669 msg = msg.MakeLower();
671 if (msg.Find(filter) >= 0)
672 return true;
675 if (m_SelectedFilters & LOGFILTER_REVS)
677 CString msg = pTree->m_csRefHash;
678 msg = msg.MakeLower();
680 if (msg.Find(filter) >= 0)
681 return true;
683 return false;
686 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree& leafs)
688 ASSERT(!leafs.empty());
690 CString csMessage;
691 UINT mbIcon=MB_ICONQUESTION;
693 bool bIsRemoteBranch = false;
694 bool bIsBranch = false;
695 if (leafs[0]->IsFrom(L"refs/remotes/")) {bIsBranch = true; bIsRemoteBranch = true;}
696 else if (leafs[0]->IsFrom(L"refs/heads/")) {bIsBranch = true;}
698 if(bIsBranch)
700 if(leafs.size() == 1)
702 CString branchToDelete = leafs[0]->GetRefName().Mid(bIsRemoteBranch ? 13 : 11);
703 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, branchToDelete);
705 //Check if branch is fully merged in HEAD
706 if (!g_Git.IsFastForward(leafs[0]->GetRefName(), _T("HEAD")))
708 csMessage += L"\r\n\r\n";
709 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED));
710 mbIcon = MB_ICONWARNING;
713 if(bIsRemoteBranch)
715 csMessage += L"\r\n\r\n";
716 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
717 mbIcon = MB_ICONWARNING;
720 else
722 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
724 csMessage += L"\r\n\r\n";
725 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK));
726 mbIcon = MB_ICONWARNING;
728 if(bIsRemoteBranch)
730 csMessage += L"\r\n\r\n";
731 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
732 mbIcon = MB_ICONWARNING;
737 else if(leafs[0]->IsFrom(L"refs/tags/"))
739 if(leafs.size() == 1)
741 CString tagToDelete = leafs[0]->GetRefName().Mid(10);
742 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, tagToDelete);
744 else
746 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
750 return CMessageBox::Show(m_hWnd, csMessage, _T("TortoiseGit"), MB_YESNO | mbIcon) == IDYES;
754 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree& leafs)
756 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
757 if(!DoDeleteRef((*i)->GetRefName()))
758 return false;
759 return true;
762 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName)
764 bool bIsRemoteBranch = false;
765 bool bIsBranch = false;
766 if (wcsncmp(completeRefName, L"refs/remotes/",13) == 0) {bIsBranch = true; bIsRemoteBranch = true;}
767 else if (wcsncmp(completeRefName, L"refs/heads/",11) == 0) {bIsBranch = true;}
769 if (bIsRemoteBranch)
771 CString branchToDelete = completeRefName.Mid(13);
772 CString cmd;
773 CString remoteName, remoteBranchToDelete;
774 if (SplitRemoteBranchName(branchToDelete, remoteName, remoteBranchToDelete))
775 return false;
777 if (CAppUtils::IsSSHPutty())
778 CAppUtils::LaunchPAgent(NULL, &remoteName);
780 cmd.Format(L"git.exe push \"%s\" :refs/heads/%s", remoteName, remoteBranchToDelete);
782 CSysProgressDlg sysProgressDlg;
783 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
784 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS)));
785 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
786 sysProgressDlg.SetShowProgressBar(false);
787 sysProgressDlg.ShowModal(this, true);
789 CString errorMsg;
790 if (g_Git.Run(cmd, &errorMsg, CP_UTF8) != 0)
792 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
793 sysProgressDlg.Stop();
794 BringWindowToTop();
795 return false;
797 sysProgressDlg.Stop();
798 BringWindowToTop();
800 else if (bIsBranch)
802 if (g_Git.DeleteRef(completeRefName))
804 CMessageBox::Show(m_hWnd, g_Git.GetGitLastErr(L"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
805 return false;
808 else if (wcsncmp(completeRefName, L"refs/tags/", 10) == 0)
810 if (g_Git.DeleteRef(completeRefName))
812 CMessageBox::Show(m_hWnd, g_Git.GetGitLastErr(L"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
813 return false;
816 return true;
819 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
821 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
822 if(pLeaf == NULL)
823 return CString();
824 return pLeaf->GetRefName();
828 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
830 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
831 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
834 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
836 CPoint clientPoint=point;
837 m_RefTreeCtrl.ScreenToClient(&clientPoint);
839 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
840 if(hTreeItem!=NULL)
841 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
843 VectorPShadowTree tree;
844 ShowContextMenu(point,hTreeItem,tree);
847 void CBrowseRefsDlg::GetSelectedLeaves(VectorPShadowTree& selectedLeafs)
849 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
850 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
851 while (pos)
853 selectedLeafs.push_back((CShadowTree*)m_ListRefLeafs.GetItemData(m_ListRefLeafs.GetNextSelectedItem(pos)));
857 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
859 std::vector<CShadowTree*> selectedLeafs;
860 GetSelectedLeaves(selectedLeafs);
861 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
864 CString CBrowseRefsDlg::GetTwoSelectedRefs(VectorPShadowTree& selectedLeafs, const CString &lastSelected, const CString &separator)
866 ASSERT(selectedLeafs.size() == 2);
868 if (selectedLeafs.at(0)->GetRefName() == lastSelected)
869 return g_Git.StripRefName(selectedLeafs.at(1)->GetRefName()) + separator + g_Git.StripRefName(lastSelected);
870 else
871 return g_Git.StripRefName(selectedLeafs.at(0)->GetRefName()) + separator + g_Git.StripRefName(lastSelected);
874 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
876 CIconMenu popupMenu;
877 popupMenu.CreatePopupMenu();
879 bool bAddSeparator = false;
880 CString remoteName;
882 if(selectedLeafs.size()==1)
884 bAddSeparator = true;
886 bool bShowReflogOption = false;
887 bool bShowFetchOption = false;
888 bool bShowRenameOption = false;
889 bool bShowCreateBranchOption = false;
890 bool bShowEditBranchDescriptionOption = false;
892 CString fetchFromCmd;
894 if(selectedLeafs[0]->IsFrom(L"refs/heads/"))
896 bShowReflogOption = true;
897 bShowRenameOption = true;
898 bShowEditBranchDescriptionOption = true;
900 else if(selectedLeafs[0]->IsFrom(L"refs/remotes/"))
902 bShowReflogOption = true;
903 bShowFetchOption = true;
904 bShowCreateBranchOption = true;
906 CString remoteBranch;
907 if (SplitRemoteBranchName(selectedLeafs[0]->GetRefName(), remoteName, remoteBranch))
908 bShowFetchOption = false;
909 else
910 fetchFromCmd.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
912 else if(selectedLeafs[0]->IsFrom(L"refs/tags/"))
916 CString temp;
917 temp.LoadString(IDS_MENULOG);
918 popupMenu.AppendMenuIcon(eCmd_ViewLog, temp, IDI_LOG);
919 popupMenu.AppendMenuIcon(eCmd_RepoBrowser, IDS_LOG_BROWSEREPO, IDI_REPOBROWSE);
920 if(bShowReflogOption)
922 temp.LoadString(IDS_MENUREFLOG);
923 popupMenu.AppendMenuIcon(eCmd_ShowReflog, temp, IDI_LOG);
926 popupMenu.AppendMenu(MF_SEPARATOR);
927 bAddSeparator = false;
929 if(bShowFetchOption)
931 bAddSeparator = true;
932 popupMenu.AppendMenuIcon(eCmd_Fetch, fetchFromCmd, IDI_PULL);
935 if(bAddSeparator)
936 popupMenu.AppendMenu(MF_SEPARATOR);
938 bAddSeparator = false;
939 if (m_bHasWC)
941 CString format, str;
942 if (selectedLeafs[0]->GetRefName() != _T("refs/heads/") + g_Git.GetCurrentBranch())
944 format.LoadString(IDS_LOG_POPUP_MERGEREV);
945 str.Format(format, g_Git.GetCurrentBranch());
946 popupMenu.AppendMenuIcon(eCmd_Merge, str, IDI_MERGE);
948 popupMenu.AppendMenuIcon(eCmd_Switch, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS)), IDI_SWITCH);
949 popupMenu.AppendMenu(MF_SEPARATOR);
952 if(bShowCreateBranchOption)
954 bAddSeparator = true;
955 temp.LoadString(IDS_MENUBRANCH);
956 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
959 if (bShowEditBranchDescriptionOption)
961 bAddSeparator = true;
962 popupMenu.AppendMenuIcon(eCmd_EditBranchDescription, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION)), IDI_RENAME);
964 if(bShowRenameOption)
966 bAddSeparator = true;
967 popupMenu.AppendMenuIcon(eCmd_Rename, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME)), IDI_RENAME);
970 else if(selectedLeafs.size() == 2)
972 bAddSeparator = true;
973 popupMenu.AppendMenuIcon(eCmd_Diff, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS)), IDI_DIFF);
974 popupMenu.AppendMenuIcon(eCmd_UnifiedDiff, CString(MAKEINTRESOURCE(IDS_LOG_POPUP_GNUDIFF)), IDI_DIFF);
975 CString menu;
976 menu.Format(IDS_SHOWLOG_OF, GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("..")));
977 popupMenu.AppendMenuIcon(eCmd_ViewLogRange, menu, IDI_LOG);
978 menu.Format(IDS_SHOWLOG_OF, GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("...")));
979 popupMenu.AppendMenuIcon(eCmd_ViewLogRangeReachableFromOnlyOne, menu, IDI_LOG);
982 if(!selectedLeafs.empty())
984 if(AreAllFrom(selectedLeafs, L"refs/remotes/"))
986 if(bAddSeparator)
987 popupMenu.AppendMenu(MF_SEPARATOR);
988 CString menuItemName;
989 if(selectedLeafs.size() == 1)
990 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH);
991 else
992 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES, selectedLeafs.size());
994 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteBranch, menuItemName, IDI_DELETE);
995 bAddSeparator = true;
997 else if(AreAllFrom(selectedLeafs, L"refs/heads/"))
999 if(bAddSeparator)
1000 popupMenu.AppendMenu(MF_SEPARATOR);
1001 CString menuItemName;
1002 if(selectedLeafs.size() == 1)
1003 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH);
1004 else
1005 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES, selectedLeafs.size());
1007 popupMenu.AppendMenuIcon(eCmd_DeleteBranch, menuItemName, IDI_DELETE);
1008 bAddSeparator = true;
1010 else if(AreAllFrom(selectedLeafs, L"refs/tags/"))
1012 if(bAddSeparator)
1013 popupMenu.AppendMenu(MF_SEPARATOR);
1014 CString menuItemName;
1015 if(selectedLeafs.size() == 1)
1016 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETETAG);
1017 else
1018 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETETAGS, selectedLeafs.size());
1020 popupMenu.AppendMenuIcon(eCmd_DeleteTag, menuItemName, IDI_DELETE);
1021 bAddSeparator = true;
1026 if(hTreePos!=NULL && selectedLeafs.empty())
1028 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);
1029 if(pTree->IsFrom(L"refs/remotes"))
1031 if(bAddSeparator)
1032 popupMenu.AppendMenu(MF_SEPARATOR);
1033 popupMenu.AppendMenuIcon(eCmd_ManageRemotes, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES)), IDI_SETTINGS);
1034 bAddSeparator = true;
1035 if(selectedLeafs.empty())
1037 CString remoteBranch;
1038 if (SplitRemoteBranchName(pTree->GetRefName(), remoteName, remoteBranch))
1039 remoteName = _T("");
1040 if(!remoteName.IsEmpty())
1042 CString temp;
1043 temp.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
1044 popupMenu.AppendMenuIcon(eCmd_Fetch, temp, IDI_PULL);
1046 temp.LoadString(IDS_DELETEREMOTETAG);
1047 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteTag, temp, IDI_DELETE);
1051 if(pTree->IsFrom(L"refs/heads"))
1053 if(bAddSeparator)
1054 popupMenu.AppendMenu(MF_SEPARATOR);
1055 CString temp;
1056 temp.LoadString(IDS_MENUBRANCH);
1057 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
1059 if(pTree->IsFrom(L"refs/tags"))
1061 if(bAddSeparator)
1062 popupMenu.AppendMenu(MF_SEPARATOR);
1063 CString temp;
1064 temp.LoadString(IDS_MENUTAG);
1065 popupMenu.AppendMenuIcon(eCmd_CreateTag, temp, IDI_TAG);
1066 temp.LoadString(IDS_PROC_BROWSEREFS_DELETEALLTAGS);
1067 popupMenu.AppendMenuIcon(eCmd_DeleteAllTags, temp, IDI_DELETE);
1072 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
1073 switch(cmd)
1075 case eCmd_ViewLog:
1077 CLogDlg dlg;
1078 dlg.SetRange(g_Git.FixBranchName(selectedLeafs[0]->GetRefName()));
1079 dlg.DoModal();
1081 break;
1082 case eCmd_ViewLogRange:
1084 CLogDlg dlg;
1085 dlg.SetRange(GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("..")));
1086 dlg.DoModal();
1088 break;
1089 case eCmd_ViewLogRangeReachableFromOnlyOne:
1091 CLogDlg dlg;
1092 dlg.SetRange(GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("...")));
1093 dlg.DoModal();
1095 break;
1096 case eCmd_RepoBrowser:
1097 CAppUtils::RunTortoiseGitProc(_T("/command:repobrowser /path:\"") + g_Git.m_CurrentDir + _T("\" /rev:") + selectedLeafs[0]->GetRefName());
1098 break;
1099 case eCmd_DeleteBranch:
1100 case eCmd_DeleteRemoteBranch:
1102 if(ConfirmDeleteRef(selectedLeafs))
1103 DoDeleteRefs(selectedLeafs);
1104 Refresh();
1106 break;
1107 case eCmd_DeleteTag:
1109 if(ConfirmDeleteRef(selectedLeafs))
1110 DoDeleteRefs(selectedLeafs);
1111 Refresh();
1113 break;
1114 case eCmd_ShowReflog:
1116 CRefLogDlg refLogDlg(this);
1117 refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();
1118 refLogDlg.DoModal();
1120 break;
1121 case eCmd_Fetch:
1123 CAppUtils::Fetch(remoteName);
1124 Refresh();
1126 break;
1127 case eCmd_DeleteRemoteTag:
1129 CDeleteRemoteTagDlg deleteRemoteTagDlg;
1130 deleteRemoteTagDlg.m_sRemote = remoteName;
1131 deleteRemoteTagDlg.DoModal();
1133 break;
1134 case eCmd_Merge:
1136 CString ref = selectedLeafs[0]->GetRefName();
1137 CAppUtils::Merge(&ref);
1139 break;
1140 case eCmd_Switch:
1142 CAppUtils::Switch(selectedLeafs[0]->GetRefName());
1144 break;
1145 case eCmd_Rename:
1147 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1148 if(pos != NULL)
1149 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1151 break;
1152 case eCmd_AddRemote:
1154 CAddRemoteDlg(this).DoModal();
1155 Refresh();
1157 break;
1158 case eCmd_ManageRemotes:
1160 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS)), new CSettingGitRemote(g_Git.m_CurrentDir), this).DoModal();
1161 // CSettingGitRemote W_Remotes(m_cmdPath);
1162 // W_Remotes.DoModal();
1163 Refresh();
1165 break;
1166 case eCmd_CreateBranch:
1168 CString *commitHash = NULL;
1169 if (selectedLeafs.size() == 1)
1170 commitHash = &(selectedLeafs[0]->m_csRefHash);
1171 CAppUtils::CreateBranchTag(false, commitHash);
1172 Refresh();
1174 break;
1175 case eCmd_CreateTag:
1177 CAppUtils::CreateBranchTag(true);
1178 Refresh();
1180 break;
1181 case eCmd_DeleteAllTags:
1183 for (int i = 0; i < m_ListRefLeafs.GetItemCount(); ++i)
1185 m_ListRefLeafs.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
1186 selectedLeafs.push_back((CShadowTree*)m_ListRefLeafs.GetItemData(i));
1188 if (ConfirmDeleteRef(selectedLeafs))
1189 DoDeleteRefs(selectedLeafs);
1190 Refresh();
1192 break;
1193 case eCmd_Diff:
1195 CFileDiffDlg dlg;
1196 dlg.SetDiff(
1197 NULL,
1198 selectedLeafs[1]->GetRefName() + L"^{}",
1199 selectedLeafs[0]->GetRefName() + L"^{}");
1200 dlg.DoModal();
1202 break;
1203 case eCmd_UnifiedDiff:
1205 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), selectedLeafs[0]->m_csRefHash, CTGitPath(), selectedLeafs[1]->m_csRefHash);
1207 break;
1208 case eCmd_EditBranchDescription:
1210 CInputDlg dlg;
1211 dlg.m_sHintText = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1212 dlg.m_sInputText = selectedLeafs[0]->m_csDescription;
1213 dlg.m_sTitle = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1214 dlg.m_bUseLogWidth = true;
1215 if(dlg.DoModal() == IDOK)
1217 CString key;
1218 key.Format(_T("branch.%s.description"), selectedLeafs[0]->m_csRefName);
1219 dlg.m_sInputText.Replace(_T("\r"), _T(""));
1220 dlg.m_sInputText.Trim();
1221 if (dlg.m_sInputText.IsEmpty())
1222 g_Git.UnsetConfigValue(key);
1223 else
1224 g_Git.SetConfigValue(key, dlg.m_sInputText);
1225 Refresh();
1228 break;
1232 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree& leafs, const wchar_t* from)
1234 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
1235 if(!(*i)->IsFrom(from))
1236 return false;
1237 return true;
1240 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
1242 if (pMsg->message == WM_KEYDOWN)
1244 switch (pMsg->wParam)
1246 /* case VK_RETURN:
1248 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1250 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1252 PostMessage(WM_COMMAND, IDOK);
1254 return TRUE;
1257 break;
1258 */ case VK_F2:
1260 if(pMsg->hwnd == m_ListRefLeafs.m_hWnd)
1262 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1263 if(pos != NULL)
1264 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1267 break;
1269 case VK_F5:
1271 Refresh();
1273 break;
1278 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
1281 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1283 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1284 *pResult = 0;
1286 if(m_currSortCol == pNMLV->iSubItem)
1287 m_currSortDesc = !m_currSortDesc;
1288 else
1290 m_currSortCol = pNMLV->iSubItem;
1291 m_currSortDesc = false;
1294 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
1295 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
1297 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
1300 void CBrowseRefsDlg::OnDestroy()
1302 if (!m_bPickedRefSet)
1303 m_pickedRef = GetSelectedRef(true, false);
1305 int maxcol = m_ColumnManager.GetColumnCount();
1306 for (int col = 0; col < maxcol; ++col)
1307 if (m_ColumnManager.IsVisible(col))
1308 m_ColumnManager.ColumnResized(col);
1309 m_ColumnManager.WriteSettings();
1311 CResizableStandAloneDialog::OnDestroy();
1314 void CBrowseRefsDlg::OnItemChangedListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1316 LPNMLISTVIEW pNMListView = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1317 *pResult = 0;
1319 CShadowTree *item = (CShadowTree*)m_ListRefLeafs.GetItemData(pNMListView->iItem);
1320 if (item && pNMListView->uNewState == LVIS_SELECTED)
1321 m_sLastSelected = item->GetRefName();
1323 UpdateInfoLabel();
1326 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR * /*pNMHDR*/, LRESULT *pResult)
1328 *pResult = 0;
1330 if (!m_ListRefLeafs.GetFirstSelectedItemPosition())
1331 return;
1332 EndDialog(IDOK);
1335 CString CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef, int pickRef_Kind, bool pickMultipleRefsOrRange)
1337 CBrowseRefsDlg dlg(CString(),NULL);
1339 if(initialRef.IsEmpty())
1340 initialRef = L"HEAD";
1341 dlg.m_initialRef = initialRef;
1342 dlg.m_pickRef_Kind = pickRef_Kind;
1343 dlg.m_bPickOne = !pickMultipleRefsOrRange;
1345 if(dlg.DoModal() != IDOK)
1346 return CString();
1348 return dlg.m_pickedRef;
1351 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)
1353 CString origRef;
1354 pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);
1355 CString resultRef = PickRef(false,origRef,pickRef_Kind);
1356 if(resultRef.IsEmpty())
1357 return false;
1358 if(wcsncmp(resultRef,L"refs/",5)==0)
1359 resultRef = resultRef.Mid(5);
1360 // if(wcsncmp(resultRef,L"heads/",6)==0)
1361 // resultRef = resultRef.Mid(6);
1363 //Find closest match of choice in combobox
1364 int ixFound = -1;
1365 int matchLength = 0;
1366 CString comboRefName;
1367 for(int i = 0; i < pComboBox->GetCount(); ++i)
1369 pComboBox->GetLBText(i, comboRefName);
1370 if(comboRefName.Find(L'/') < 0 && !comboRefName.IsEmpty())
1371 comboRefName.Insert(0,L"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1372 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)
1374 matchLength = comboRefName.GetLength();
1375 ixFound = i;
1378 if(ixFound >= 0)
1379 pComboBox->SetCurSel(ixFound);
1380 else
1381 ASSERT(FALSE);//No match found. So either pickRef_Kind is wrong or the combobox does not contain the ref specified in the picker (which it should unless the repo has changed before creating the CBrowseRef dialog)
1383 return true;
1386 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1388 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1389 *pResult = FALSE;
1391 if(pDispInfo->item.pszText == NULL)
1392 return; //User canceled changing
1394 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1396 if(!pTree->IsFrom(L"refs/heads/"))
1398 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES, IDS_APPNAME, MB_OK | MB_ICONERROR);
1399 return;
1402 CString origName = pTree->GetRefName().Mid(11);
1404 CString newName;
1405 if(m_pListCtrlRoot != NULL)
1406 newName = m_pListCtrlRoot->GetRefName() + L'/';
1407 newName += pDispInfo->item.pszText;
1409 if(wcsncmp(newName,L"refs/heads/",11)!=0)
1411 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE, IDS_APPNAME, MB_OK | MB_ICONERROR);
1412 return;
1415 CString newNameTrunced = newName.Mid(11);
1417 CString errorMsg;
1418 if(g_Git.Run(L"git.exe branch -m \"" + origName + L"\" \"" + newNameTrunced + L"\"", &errorMsg, CP_UTF8) != 0)
1420 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1421 return;
1423 //Do as if it failed to rename. Let Refresh() do the job.
1424 //*pResult = TRUE;
1426 Refresh(newName);
1428 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1430 // AfxMessageBox(W_csPopup);
1434 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1436 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1437 *pResult = FALSE;
1439 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1441 if(!pTree->IsFrom(L"refs/heads/"))
1443 *pResult = TRUE; //Dont allow renaming any other things then branches at the moment.
1444 return;
1448 void CBrowseRefsDlg::OnEnChangeEditFilter()
1450 SetTimer(IDT_FILTER, 1000, NULL);
1453 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent)
1455 if (nIDEvent == IDT_FILTER)
1457 KillTimer(IDT_FILTER);
1458 FillListCtrlForTreeNode(m_RefTreeCtrl.GetSelectedItem());
1461 CResizableStandAloneDialog::OnTimer(nIDEvent);
1464 LRESULT CBrowseRefsDlg::OnClickedInfoIcon(WPARAM /*wParam*/, LPARAM lParam)
1466 // FIXME: x64 version would get this function called with unexpected parameters.
1467 if (!lParam)
1468 return 0;
1470 RECT * rect = (LPRECT)lParam;
1471 CPoint point;
1472 CString temp;
1473 point = CPoint(rect->left, rect->bottom);
1474 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1475 CMenu popup;
1476 if (popup.CreatePopupMenu())
1478 temp.LoadString(IDS_LOG_FILTER_REFNAME);
1479 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME), LOGFILTER_REFNAME, temp);
1481 temp.LoadString(IDS_LOG_FILTER_SUBJECT);
1482 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT), LOGFILTER_SUBJECT, temp);
1484 temp.LoadString(IDS_LOG_FILTER_AUTHORS);
1485 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS), LOGFILTER_AUTHORS, temp);
1487 temp.LoadString(IDS_LOG_FILTER_REVS);
1488 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS), LOGFILTER_REVS, temp);
1490 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1491 if (selection != 0)
1493 m_SelectedFilters ^= selection;
1494 SetFilterCueText();
1495 SetTimer(IDT_FILTER, 1000, NULL);
1498 return 0L;
1501 void CBrowseRefsDlg::SetFilterCueText()
1503 CString temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY));
1504 temp += _T(" ");
1506 if (m_SelectedFilters & LOGFILTER_REFNAME)
1507 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME));
1509 if (m_SelectedFilters & LOGFILTER_SUBJECT)
1511 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1512 temp += _T(", ");
1513 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT));
1516 if (m_SelectedFilters & LOGFILTER_AUTHORS)
1518 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1519 temp += _T(", ");
1520 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS));
1523 if (m_SelectedFilters & LOGFILTER_REVS)
1525 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1526 temp += _T(", ");
1527 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS));
1530 // to make the cue banner text appear more to the right of the edit control
1531 temp = _T(" ") + temp;
1532 m_ctrlFilter.SetCueBanner(temp.TrimRight());
1535 void CBrowseRefsDlg::OnBnClickedCurrentbranch()
1537 m_pickedRef = g_Git.GetCurrentBranch(true);
1538 m_bPickedRefSet = true;
1539 OnOK();
1542 void CBrowseRefsDlg::UpdateInfoLabel()
1544 CString temp;
1545 temp.FormatMessage(IDS_REFBROWSE_INFO, m_ListRefLeafs.GetItemCount(), m_ListRefLeafs.GetSelectedCount());
1546 SetDlgItemText(IDC_INFOLABEL, temp);