Make parameters const
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blobaf1096fa7b7c5aa067083d983639d737f5505890
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_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);
127 case CBrowseRefsDlg::eCol_Msg: return SortStrCmp(pLeft->m_csSubject, pRight->m_csSubject);
128 case CBrowseRefsDlg::eCol_LastAuthor: return SortStrCmp(pLeft->m_csAuthor, pRight->m_csAuthor);
129 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);
130 case CBrowseRefsDlg::eCol_Description: return SortStrCmp(pLeft->m_csDescription, pRight->m_csDescription);
132 return 0;
134 int SortStrCmp(const CString& left, const CString& right)
136 if (m_bSortLogical)
137 return StrCmpLogicalW(left, right);
138 return StrCmpI(left, right);
141 int m_col;
142 bool m_desc;
143 CListCtrl* m_pList;
144 bool m_bSortLogical;
147 // CBrowseRefsDlg dialog
149 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)
151 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)
152 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),
153 m_cmdPath(cmdPath),
154 m_currSortCol(0),
155 m_currSortDesc(false),
156 m_initialRef(L"HEAD"),
157 m_pickRef_Kind(gPickRef_All),
158 m_pListCtrlRoot(NULL),
159 m_bHasWC(true),
160 m_SelectedFilters(LOGFILTER_ALL),
161 m_ColumnManager(&m_ListRefLeafs),
162 m_bPickOne(false),
163 m_bPickedRefSet(false)
168 CBrowseRefsDlg::~CBrowseRefsDlg()
172 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)
174 CDialog::DoDataExchange(pDX);
175 DDX_Control(pDX, IDC_TREE_REF, m_RefTreeCtrl);
176 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_ListRefLeafs);
177 DDX_Control(pDX, IDC_BROWSEREFS_EDIT_FILTER, m_ctrlFilter);
181 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)
182 ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)
183 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)
184 ON_WM_CONTEXTMENU()
185 ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)
186 ON_WM_DESTROY()
187 ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)
188 ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnItemChangedListRefLeafs)
189 ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs)
190 ON_NOTIFY(LVN_BEGINLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs)
191 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER, &CBrowseRefsDlg::OnEnChangeEditFilter)
192 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED, OnClickedInfoIcon)
193 ON_WM_TIMER()
194 ON_BN_CLICKED(IDC_CURRENTBRANCH, OnBnClickedCurrentbranch)
195 END_MESSAGE_MAP()
198 // CBrowseRefsDlg message handlers
200 void CBrowseRefsDlg::OnBnClickedOk()
202 if (m_bPickOne || m_ListRefLeafs.GetSelectedCount() != 2)
204 OnOK();
205 return;
208 CIconMenu popupMenu;
209 popupMenu.CreatePopupMenu();
211 std::vector<CShadowTree*> selectedLeafs;
212 GetSelectedLeaves(selectedLeafs);
214 popupMenu.AppendMenuIcon(1, GetSelectedRef(true, false), IDI_LOG);
215 popupMenu.SetDefaultItem(1);
216 popupMenu.AppendMenuIcon(2, GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("..")), IDI_LOG);
217 popupMenu.AppendMenuIcon(3, GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("...")), IDI_LOG);
219 RECT rect;
220 GetDlgItem(IDOK)->GetWindowRect(&rect);
221 int selection = popupMenu.TrackPopupMenuEx(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, rect.left, rect.top, this, 0);
222 switch (selection)
224 case 1:
225 OnOK();
226 break;
227 case 2:
229 m_bPickedRefSet = true;
230 m_pickedRef = GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T(".."));
231 OnOK();
233 break;
234 case 3:
236 m_bPickedRefSet = true;
237 m_pickedRef = GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("..."));
238 OnOK();
240 break;
241 default:
242 break;
246 BOOL CBrowseRefsDlg::OnInitDialog()
248 CResizableStandAloneDialog::OnInitDialog();
249 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
251 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
252 m_ctrlFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
253 m_ctrlFilter.SetInfoIcon(IDI_FILTEREDIT);
254 SetFilterCueText();
256 AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);
257 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
258 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER, TOP_LEFT);
259 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER, TOP_LEFT, TOP_RIGHT);
260 AddAnchor(IDHELP, BOTTOM_RIGHT);
262 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
263 static UINT columnNames[] = { IDS_BRANCHNAME, IDS_DATELASTCOMMIT, IDS_LASTCOMMIT, IDS_LASTAUTHOR, IDS_HASH, IDS_DESCRIPTION };
264 static int columnWidths[] = { 150, 100, 300, 100, 80, 80 };
265 DWORD dwDefaultColumns = (1 << eCol_Name) | (1 << eCol_Date) | (1 << eCol_Msg) |
266 (1 << eCol_LastAuthor) | (1 << eCol_Hash) | (1 << eCol_Description);
267 m_ColumnManager.SetNames(columnNames, _countof(columnNames));
268 m_ColumnManager.ReadSettings(dwDefaultColumns, 0, _T("BrowseRefs"), _countof(columnNames), columnWidths);
269 m_bPickedRefSet = false;
271 AddAnchor(IDOK,BOTTOM_RIGHT);
272 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
273 AddAnchor(IDC_CURRENTBRANCH, BOTTOM_RIGHT);
275 Refresh(m_initialRef);
277 EnableSaveRestore(L"BrowseRefs");
279 CString sWindowTitle;
280 GetWindowText(sWindowTitle);
281 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
283 m_bHasWC = !g_GitAdminDir.IsBareRepo(g_Git.m_CurrentDir);
285 if (m_bPickOne)
286 m_ListRefLeafs.ModifyStyle(0, LVS_SINGLESEL);
288 m_ListRefLeafs.SetFocus();
289 return FALSE;
292 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
294 int posSlash=nameLeft.Find('/');
295 CString nameSub;
296 if(posSlash<0)
298 nameSub=nameLeft;
299 nameLeft.Empty();//Nothing left
301 else
303 nameSub=nameLeft.Left(posSlash);
304 nameLeft=nameLeft.Mid(posSlash+1);
306 if(nameSub.IsEmpty())
307 return NULL;
309 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
310 return NULL;
312 CShadowTree& nextNode=m_ShadowTree[nameSub];
313 nextNode.m_csRefName=nameSub;
314 nextNode.m_pParent=this;
315 return &nextNode;
318 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)
320 if(IsLeaf())
322 if(m_csRefName.GetLength() > partialRefName.GetLength())
323 return NULL;
324 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)
326 //Match of leaf name. Try match on total name.
327 CString totalRefName = GetRefName();
328 if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)
329 return this; //Also match. Found.
332 else
334 //Not a leaf. Search all nodes.
335 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)
337 CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);
338 if(pSubtree != NULL)
339 return pSubtree; //Found
342 return NULL;//Not found
346 typedef std::map<CString,CString> MAP_STRING_STRING;
348 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf, bool pickFirstSelIfMultiSel)
350 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
351 //List ctrl selection?
352 if(pos && (pickFirstSelIfMultiSel || m_ListRefLeafs.GetSelectedCount() == 1))
354 //A leaf is selected
355 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(
356 m_ListRefLeafs.GetNextSelectedItem(pos));
357 return pTree->GetRefName();
359 else if (pos && !pickFirstSelIfMultiSel)
361 // at least one leaf is selected
362 CString refs;
363 int index;
364 while ((index = m_ListRefLeafs.GetNextSelectedItem(pos)) >= 0)
366 CString ref = ((CShadowTree*)m_ListRefLeafs.GetItemData(index))->GetRefName();
367 if(wcsncmp(ref, L"refs/", 5) == 0)
368 ref = ref.Mid(5);
369 if(wcsncmp(ref, L"heads/", 6) == 0)
370 ref = ref.Mid(6);
371 refs += ref + _T(" ");
373 return refs.Trim();
375 else if(!onlyIfLeaf)
377 //Tree ctrl selection?
378 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
379 if(hTree!=NULL)
381 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);
382 return pTree->GetRefName();
385 return CString();//None
388 static int GetBranchDescriptionsCallback(const git_config_entry *entry, void *data)
390 MAP_STRING_STRING *descriptions = (MAP_STRING_STRING *) data;
391 CString key = CUnicodeUtils::GetUnicode(entry->name, CP_UTF8);
392 CString val = CUnicodeUtils::GetUnicode(entry->value, CP_UTF8);
393 descriptions->insert(std::make_pair(key.Mid(7, key.GetLength() - 7 - 12), val)); // 7: branch., 12: .description
394 return 0;
397 MAP_STRING_STRING GetBranchDescriptions()
399 MAP_STRING_STRING descriptions;
400 git_config * config;
401 git_config_new(&config);
402 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL, FALSE);
403 git_config_foreach_match(config, "branch\\..*\\.description", GetBranchDescriptionsCallback, &descriptions);
404 git_config_free(config);
405 return descriptions;
408 void CBrowseRefsDlg::Refresh(CString selectRef)
410 // m_RefMap.clear();
411 // g_Git.GetMapHashToFriendName(m_RefMap);
413 if(!selectRef.IsEmpty())
415 if(selectRef == "HEAD")
417 selectRef = g_Git.GetSymbolicRef(selectRef, false);
420 else
422 selectRef = GetSelectedRef(false, true);
425 m_RefTreeCtrl.DeleteAllItems();
426 m_ListRefLeafs.DeleteAllItems();
427 m_TreeRoot.m_ShadowTree.clear();
428 m_TreeRoot.m_csRefName = "refs";
429 m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);
430 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);
432 CString allRefs, error;
433 if (g_Git.Run(L"git.exe for-each-ref --format="
434 L"%(refname)%04"
435 L"%(objectname)%04"
436 L"%(authordate:relative)%04"
437 L"%(subject)%04"
438 L"%(authorname)%04"
439 L"%(authordate:iso8601)%03",
440 &allRefs, &error, CP_UTF8))
442 CMessageBox::Show(NULL, CString(_T("Get refs failed\n")) + error, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
445 int linePos=0;
446 CString singleRef;
448 MAP_STRING_STRING refMap;
450 //First sort on ref name
451 while(!(singleRef=allRefs.Tokenize(L"\03",linePos)).IsEmpty())
453 singleRef.TrimLeft(L"\r\n");
454 int valuePos=0;
455 CString refName=singleRef.Tokenize(L"\04",valuePos);
456 if(refName.IsEmpty())
457 continue;
458 CString refRest=singleRef.Mid(valuePos);
461 //Use ref based on m_pickRef_Kind
462 if (wcsncmp(refName, L"refs/heads/", 11) == 0 && !(m_pickRef_Kind & gPickRef_Head))
463 continue; //Skip
464 if (wcsncmp(refName, L"refs/tags/", 10) == 0 && !(m_pickRef_Kind & gPickRef_Tag))
465 continue; //Skip
466 if (wcsncmp(refName, L"refs/remotes/", 13) == 0 && !(m_pickRef_Kind & gPickRef_Remote))
467 continue; //Skip
469 refMap[refName] = refRest; //Use
472 MAP_STRING_STRING descriptions = GetBranchDescriptions();
474 //Populate ref tree
475 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
477 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
478 CString values=iterRefMap->second;
479 values.Replace(L"\04" L"\04",L"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
481 int valuePos=0;
482 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
483 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
484 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
485 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
486 treeLeaf.m_csDate_Iso8601= values.Tokenize(L"\04",valuePos);
488 if (wcsncmp(iterRefMap->first, L"refs/heads/", 11) == 0)
489 treeLeaf.m_csDescription = descriptions[treeLeaf.m_csRefName];
492 // try exact match first
493 if(selectRef.IsEmpty() || !(SelectRef(selectRef, true) || SelectRef(selectRef, false)))
494 //Probably not on a branch. Select root node.
495 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);
499 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
501 if(!bExactMatch)
503 CString newRefName = GetFullRefName(refName);
504 if(!newRefName.IsEmpty())
505 refName = newRefName;
506 //else refName is not a valid ref. Try to select as good as possible.
508 if(_wcsnicmp(refName, L"refs/", 5) != 0)
509 return false; // Not a ref name
511 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
512 if(treeLeafHead.m_hTree != NULL)
514 //Not a leaf. Select tree node and return
515 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
516 return true;
519 if(treeLeafHead.m_pParent==NULL)
520 return false; //Weird... should not occur.
522 //This is the current head.
523 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
525 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
527 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
528 if(pCurrShadowTree == &treeLeafHead)
530 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
531 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
535 return true;
538 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
540 if(pTreePos==NULL)
542 if(_wcsnicmp(refName, L"refs/", 5) == 0)
543 refName=refName.Mid(5);
544 pTreePos=&m_TreeRoot;
546 if(refName.IsEmpty())
547 return *pTreePos;//Found leaf
549 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
550 if(pNextTree==NULL)
552 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
553 ASSERT(!bCreateIfNotExist);
554 return *pTreePos;
557 if(!refName.IsEmpty())
559 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
560 //Leafs are for the list control.
561 if(pNextTree->m_hTree==NULL)
563 //New tree. Create node in control.
564 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
565 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
569 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
573 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
575 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
576 *pResult = 0;
578 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
581 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
583 m_ListRefLeafs.DeleteAllItems();
585 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
586 if(pTree==NULL)
588 ASSERT(FALSE);
589 return;
591 FillListCtrlForShadowTree(pTree,L"",true);
594 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
596 if(pTree->IsLeaf())
598 CString filter;
599 m_ctrlFilter.GetWindowText(filter);
600 filter.MakeLower();
601 CString ref = refNamePrefix + pTree->m_csRefName;
602 if (!(pTree->m_csRefName.IsEmpty() || pTree->m_csRefName == "refs" && pTree->m_pParent == NULL) && IsMatchFilter(pTree, ref, filter))
604 int indexItem = m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(), L"");
606 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
607 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, ref);
608 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);
609 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
610 m_ListRefLeafs.SetItemText(indexItem,eCol_LastAuthor, pTree->m_csAuthor);
611 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
612 int pos = 0;
613 m_ListRefLeafs.SetItemText(indexItem,eCol_Description, pTree->m_csDescription.Tokenize(_T("\n"), pos));
616 else
619 CString csThisName;
620 if(!isFirstLevel)
621 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
622 else
623 m_pListCtrlRoot = pTree;
624 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
626 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
629 if (isFirstLevel)
631 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
632 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
634 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
638 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree* pTree, const CString &ref, const CString &filter)
640 if (m_SelectedFilters & LOGFILTER_REFNAME)
642 CString msg = ref;
643 msg = msg.MakeLower();
645 if (msg.Find(filter) >= 0)
646 return true;
649 if (m_SelectedFilters & LOGFILTER_SUBJECT)
651 CString msg = pTree->m_csSubject;
652 msg = msg.MakeLower();
654 if (msg.Find(filter) >= 0)
655 return true;
658 if (m_SelectedFilters & LOGFILTER_AUTHORS)
660 CString msg = pTree->m_csAuthor;
661 msg = msg.MakeLower();
663 if (msg.Find(filter) >= 0)
664 return true;
667 if (m_SelectedFilters & LOGFILTER_REVS)
669 CString msg = pTree->m_csRefHash;
670 msg = msg.MakeLower();
672 if (msg.Find(filter) >= 0)
673 return true;
675 return false;
678 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree& leafs)
680 ASSERT(!leafs.empty());
682 CString csMessage;
683 UINT mbIcon=MB_ICONQUESTION;
685 bool bIsRemoteBranch = false;
686 bool bIsBranch = false;
687 if (leafs[0]->IsFrom(L"refs/remotes/")) {bIsBranch = true; bIsRemoteBranch = true;}
688 else if (leafs[0]->IsFrom(L"refs/heads/")) {bIsBranch = true;}
690 if(bIsBranch)
692 if(leafs.size() == 1)
694 CString branchToDelete = leafs[0]->GetRefName().Mid(bIsRemoteBranch ? 13 : 11);
695 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, branchToDelete);
697 //Check if branch is fully merged in HEAD
698 if (!g_Git.IsFastForward(leafs[0]->GetRefName(), _T("HEAD")))
700 csMessage += L"\r\n\r\n";
701 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED));
702 mbIcon = MB_ICONWARNING;
705 if(bIsRemoteBranch)
707 csMessage += L"\r\n\r\n";
708 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
709 mbIcon = MB_ICONWARNING;
712 else
714 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
716 csMessage += L"\r\n\r\n";
717 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK));
718 mbIcon = MB_ICONWARNING;
720 if(bIsRemoteBranch)
722 csMessage += L"\r\n\r\n";
723 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
724 mbIcon = MB_ICONWARNING;
729 else if(leafs[0]->IsFrom(L"refs/tags/"))
731 if(leafs.size() == 1)
733 CString tagToDelete = leafs[0]->GetRefName().Mid(10);
734 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, tagToDelete);
736 else
738 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
742 return CMessageBox::Show(m_hWnd, csMessage, _T("TortoiseGit"), MB_YESNO | mbIcon) == IDYES;
746 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree& leafs, bool bForce)
748 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
749 if(!DoDeleteRef((*i)->GetRefName(), bForce))
750 return false;
751 return true;
754 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)
756 bool bIsRemoteBranch = false;
757 bool bIsBranch = false;
758 if (wcsncmp(completeRefName, L"refs/remotes/",13) == 0) {bIsBranch = true; bIsRemoteBranch = true;}
759 else if (wcsncmp(completeRefName, L"refs/heads/",11) == 0) {bIsBranch = true;}
761 if(bIsBranch)
763 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);
764 CString cmd;
765 if(bIsRemoteBranch)
767 CString remoteName, remoteBranchToDelete;
768 if (SplitRemoteBranchName(branchToDelete, remoteName, remoteBranchToDelete))
769 return false;
771 if(CAppUtils::IsSSHPutty())
773 CAppUtils::LaunchPAgent(NULL, &remoteName);
776 cmd.Format(L"git.exe push \"%s\" :refs/heads/%s", remoteName, remoteBranchToDelete);
778 else
779 cmd.Format(L"git.exe branch -%c -- %s",bForce?L'D':L'd',branchToDelete);
780 CSysProgressDlg sysProgressDlg;
781 if (bIsRemoteBranch)
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 if (bIsRemoteBranch)
794 sysProgressDlg.Stop();
795 BringWindowToTop();
796 return false;
798 if (bIsRemoteBranch)
799 sysProgressDlg.Stop();
800 BringWindowToTop();
802 else if (wcsncmp(completeRefName, L"refs/tags/", 10) == 0)
804 CString tagToDelete = completeRefName.Mid(10);
805 CString cmd;
806 cmd.Format(L"git.exe tag -d -- %s",tagToDelete);
807 CString errorMsg;
808 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
810 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
811 return false;
814 return true;
817 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
819 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
820 if(pLeaf == NULL)
821 return CString();
822 return pLeaf->GetRefName();
826 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
828 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
829 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
832 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
834 CPoint clientPoint=point;
835 m_RefTreeCtrl.ScreenToClient(&clientPoint);
837 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
838 if(hTreeItem!=NULL)
839 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
841 VectorPShadowTree tree;
842 ShowContextMenu(point,hTreeItem,tree);
845 void CBrowseRefsDlg::GetSelectedLeaves(VectorPShadowTree& selectedLeafs)
847 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
848 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
849 while (pos)
851 selectedLeafs.push_back((CShadowTree*)m_ListRefLeafs.GetItemData(m_ListRefLeafs.GetNextSelectedItem(pos)));
855 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
857 std::vector<CShadowTree*> selectedLeafs;
858 GetSelectedLeaves(selectedLeafs);
859 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
862 CString CBrowseRefsDlg::GetTwoSelectedRefs(VectorPShadowTree& selectedLeafs, const CString &lastSelected, const CString &separator)
864 ASSERT(selectedLeafs.size() == 2);
866 if (selectedLeafs.at(0)->GetRefName() == lastSelected)
867 return g_Git.StripRefName(selectedLeafs.at(1)->GetRefName()) + separator + g_Git.StripRefName(lastSelected);
868 else
869 return g_Git.StripRefName(selectedLeafs.at(0)->GetRefName()) + separator + g_Git.StripRefName(lastSelected);
872 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
874 CIconMenu popupMenu;
875 popupMenu.CreatePopupMenu();
877 bool bAddSeparator = false;
878 CString remoteName;
880 if(selectedLeafs.size()==1)
882 bAddSeparator = true;
884 bool bShowReflogOption = false;
885 bool bShowFetchOption = false;
886 bool bShowRenameOption = false;
887 bool bShowCreateBranchOption = false;
888 bool bShowEditBranchDescriptionOption = false;
890 CString fetchFromCmd;
892 if(selectedLeafs[0]->IsFrom(L"refs/heads/"))
894 bShowReflogOption = true;
895 bShowRenameOption = true;
896 bShowEditBranchDescriptionOption = true;
898 else if(selectedLeafs[0]->IsFrom(L"refs/remotes/"))
900 bShowReflogOption = true;
901 bShowFetchOption = true;
902 bShowCreateBranchOption = true;
904 CString remoteBranch;
905 if (SplitRemoteBranchName(selectedLeafs[0]->GetRefName(), remoteName, remoteBranch))
906 bShowFetchOption = false;
907 else
908 fetchFromCmd.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
910 else if(selectedLeafs[0]->IsFrom(L"refs/tags/"))
914 CString temp;
915 temp.LoadString(IDS_MENULOG);
916 popupMenu.AppendMenuIcon(eCmd_ViewLog, temp, IDI_LOG);
917 popupMenu.AppendMenuIcon(eCmd_RepoBrowser, IDS_LOG_BROWSEREPO, IDI_REPOBROWSE);
918 if(bShowReflogOption)
920 temp.LoadString(IDS_MENUREFLOG);
921 popupMenu.AppendMenuIcon(eCmd_ShowReflog, temp, IDI_LOG);
924 popupMenu.AppendMenu(MF_SEPARATOR);
925 bAddSeparator = false;
927 if(bShowFetchOption)
929 bAddSeparator = true;
930 popupMenu.AppendMenuIcon(eCmd_Fetch, fetchFromCmd, IDI_PULL);
933 if(bAddSeparator)
934 popupMenu.AppendMenu(MF_SEPARATOR);
936 bAddSeparator = false;
937 if (m_bHasWC)
939 CString format, str;
940 if (selectedLeafs[0]->GetRefName() != _T("refs/heads/") + g_Git.GetCurrentBranch())
942 format.LoadString(IDS_LOG_POPUP_MERGEREV);
943 str.Format(format, g_Git.GetCurrentBranch());
944 popupMenu.AppendMenuIcon(eCmd_Merge, str, IDI_MERGE);
946 popupMenu.AppendMenuIcon(eCmd_Switch, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS)), IDI_SWITCH);
947 popupMenu.AppendMenu(MF_SEPARATOR);
950 if(bShowCreateBranchOption)
952 bAddSeparator = true;
953 temp.LoadString(IDS_MENUBRANCH);
954 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
957 if (bShowEditBranchDescriptionOption)
959 bAddSeparator = true;
960 popupMenu.AppendMenuIcon(eCmd_EditBranchDescription, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION)), IDI_RENAME);
962 if(bShowRenameOption)
964 bAddSeparator = true;
965 popupMenu.AppendMenuIcon(eCmd_Rename, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME)), IDI_RENAME);
968 else if(selectedLeafs.size() == 2)
970 bAddSeparator = true;
971 popupMenu.AppendMenuIcon(eCmd_Diff, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS)), IDI_DIFF);
972 popupMenu.AppendMenuIcon(eCmd_UnifiedDiff, CString(MAKEINTRESOURCE(IDS_LOG_POPUP_GNUDIFF)), IDI_DIFF);
973 CString menu;
974 menu.Format(IDS_SHOWLOG_OF, GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("..")));
975 popupMenu.AppendMenuIcon(eCmd_ViewLogRange, menu, IDI_LOG);
976 menu.Format(IDS_SHOWLOG_OF, GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("...")));
977 popupMenu.AppendMenuIcon(eCmd_ViewLogRangeReachableFromOnlyOne, menu, IDI_LOG);
980 if(!selectedLeafs.empty())
982 if(AreAllFrom(selectedLeafs, L"refs/remotes/"))
984 if(bAddSeparator)
985 popupMenu.AppendMenu(MF_SEPARATOR);
986 CString menuItemName;
987 if(selectedLeafs.size() == 1)
988 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH);
989 else
990 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES, selectedLeafs.size());
992 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteBranch, menuItemName, IDI_DELETE);
993 bAddSeparator = true;
995 else if(AreAllFrom(selectedLeafs, L"refs/heads/"))
997 if(bAddSeparator)
998 popupMenu.AppendMenu(MF_SEPARATOR);
999 CString menuItemName;
1000 if(selectedLeafs.size() == 1)
1001 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH);
1002 else
1003 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES, selectedLeafs.size());
1005 popupMenu.AppendMenuIcon(eCmd_DeleteBranch, menuItemName, IDI_DELETE);
1006 bAddSeparator = true;
1008 else if(AreAllFrom(selectedLeafs, L"refs/tags/"))
1010 if(bAddSeparator)
1011 popupMenu.AppendMenu(MF_SEPARATOR);
1012 CString menuItemName;
1013 if(selectedLeafs.size() == 1)
1014 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETETAG);
1015 else
1016 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETETAGS, selectedLeafs.size());
1018 popupMenu.AppendMenuIcon(eCmd_DeleteTag, menuItemName, IDI_DELETE);
1019 bAddSeparator = true;
1024 if(hTreePos!=NULL && selectedLeafs.empty())
1026 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);
1027 if(pTree->IsFrom(L"refs/remotes"))
1029 if(bAddSeparator)
1030 popupMenu.AppendMenu(MF_SEPARATOR);
1031 popupMenu.AppendMenuIcon(eCmd_ManageRemotes, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES)), IDI_SETTINGS);
1032 bAddSeparator = true;
1033 if(selectedLeafs.empty())
1035 CString remoteBranch;
1036 if (SplitRemoteBranchName(pTree->GetRefName(), remoteName, remoteBranch))
1037 remoteName = _T("");
1038 if(!remoteName.IsEmpty())
1040 CString temp;
1041 temp.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
1042 popupMenu.AppendMenuIcon(eCmd_Fetch, temp, IDI_PULL);
1044 temp.LoadString(IDS_DELETEREMOTETAG);
1045 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteTag, temp, IDI_DELETE);
1049 if(pTree->IsFrom(L"refs/heads"))
1051 if(bAddSeparator)
1052 popupMenu.AppendMenu(MF_SEPARATOR);
1053 CString temp;
1054 temp.LoadString(IDS_MENUBRANCH);
1055 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
1057 if(pTree->IsFrom(L"refs/tags"))
1059 if(bAddSeparator)
1060 popupMenu.AppendMenu(MF_SEPARATOR);
1061 CString temp;
1062 temp.LoadString(IDS_MENUTAG);
1063 popupMenu.AppendMenuIcon(eCmd_CreateTag, temp, IDI_TAG);
1064 temp.LoadString(IDS_PROC_BROWSEREFS_DELETEALLTAGS);
1065 popupMenu.AppendMenuIcon(eCmd_DeleteAllTags, temp, IDI_DELETE);
1070 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
1071 switch(cmd)
1073 case eCmd_ViewLog:
1075 CLogDlg dlg;
1076 dlg.SetRange(g_Git.FixBranchName(selectedLeafs[0]->GetRefName()));
1077 dlg.DoModal();
1079 break;
1080 case eCmd_ViewLogRange:
1082 CLogDlg dlg;
1083 dlg.SetRange(GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("..")));
1084 dlg.DoModal();
1086 break;
1087 case eCmd_ViewLogRangeReachableFromOnlyOne:
1089 CLogDlg dlg;
1090 dlg.SetRange(GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("...")));
1091 dlg.DoModal();
1093 break;
1094 case eCmd_RepoBrowser:
1095 CAppUtils::RunTortoiseGitProc(_T("/command:repobrowser /path:\"") + g_Git.m_CurrentDir + _T("\" /rev:") + selectedLeafs[0]->GetRefName());
1096 break;
1097 case eCmd_DeleteBranch:
1098 case eCmd_DeleteRemoteBranch:
1100 if(ConfirmDeleteRef(selectedLeafs))
1101 DoDeleteRefs(selectedLeafs, true);
1102 Refresh();
1104 break;
1105 case eCmd_DeleteTag:
1107 if(ConfirmDeleteRef(selectedLeafs))
1108 DoDeleteRefs(selectedLeafs, true);
1109 Refresh();
1111 break;
1112 case eCmd_ShowReflog:
1114 CRefLogDlg refLogDlg(this);
1115 refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();
1116 refLogDlg.DoModal();
1118 break;
1119 case eCmd_Fetch:
1121 CAppUtils::Fetch(remoteName);
1122 Refresh();
1124 break;
1125 case eCmd_DeleteRemoteTag:
1127 CDeleteRemoteTagDlg deleteRemoteTagDlg;
1128 deleteRemoteTagDlg.m_sRemote = remoteName;
1129 deleteRemoteTagDlg.DoModal();
1131 break;
1132 case eCmd_Merge:
1134 CString ref = selectedLeafs[0]->GetRefName();
1135 CAppUtils::Merge(&ref);
1137 break;
1138 case eCmd_Switch:
1140 CAppUtils::Switch(selectedLeafs[0]->GetRefName());
1142 break;
1143 case eCmd_Rename:
1145 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1146 if(pos != NULL)
1147 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1149 break;
1150 case eCmd_AddRemote:
1152 CAddRemoteDlg(this).DoModal();
1153 Refresh();
1155 break;
1156 case eCmd_ManageRemotes:
1158 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS)), new CSettingGitRemote(g_Git.m_CurrentDir), this).DoModal();
1159 // CSettingGitRemote W_Remotes(m_cmdPath);
1160 // W_Remotes.DoModal();
1161 Refresh();
1163 break;
1164 case eCmd_CreateBranch:
1166 CString *commitHash = NULL;
1167 if (selectedLeafs.size() == 1)
1168 commitHash = &(selectedLeafs[0]->m_csRefHash);
1169 CAppUtils::CreateBranchTag(false, commitHash);
1170 Refresh();
1172 break;
1173 case eCmd_CreateTag:
1175 CAppUtils::CreateBranchTag(true);
1176 Refresh();
1178 break;
1179 case eCmd_DeleteAllTags:
1181 for (int i = 0; i < m_ListRefLeafs.GetItemCount(); ++i)
1183 m_ListRefLeafs.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
1184 selectedLeafs.push_back((CShadowTree*)m_ListRefLeafs.GetItemData(i));
1186 if (ConfirmDeleteRef(selectedLeafs))
1187 DoDeleteRefs(selectedLeafs, true);
1188 Refresh();
1190 break;
1191 case eCmd_Diff:
1193 CFileDiffDlg dlg;
1194 dlg.SetDiff(
1195 NULL,
1196 selectedLeafs[1]->GetRefName() + L"^{}",
1197 selectedLeafs[0]->GetRefName() + L"^{}");
1198 dlg.DoModal();
1200 break;
1201 case eCmd_UnifiedDiff:
1203 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), selectedLeafs[0]->m_csRefHash, CTGitPath(), selectedLeafs[1]->m_csRefHash);
1205 break;
1206 case eCmd_EditBranchDescription:
1208 CInputDlg dlg;
1209 dlg.m_sHintText = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1210 dlg.m_sInputText = selectedLeafs[0]->m_csDescription;
1211 dlg.m_sTitle = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1212 dlg.m_bUseLogWidth = true;
1213 if(dlg.DoModal() == IDOK)
1215 CString key;
1216 key.Format(_T("branch.%s.description"), selectedLeafs[0]->m_csRefName);
1217 dlg.m_sInputText.Replace(_T("\r"), _T(""));
1218 dlg.m_sInputText.Trim();
1219 if (dlg.m_sInputText.IsEmpty())
1220 g_Git.UnsetConfigValue(key);
1221 else
1222 g_Git.SetConfigValue(key, dlg.m_sInputText);
1223 Refresh();
1226 break;
1230 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree& leafs, const wchar_t* from)
1232 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
1233 if(!(*i)->IsFrom(from))
1234 return false;
1235 return true;
1238 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
1240 if (pMsg->message == WM_KEYDOWN)
1242 switch (pMsg->wParam)
1244 /* case VK_RETURN:
1246 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1248 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1250 PostMessage(WM_COMMAND, IDOK);
1252 return TRUE;
1255 break;
1256 */ case VK_F2:
1258 if(pMsg->hwnd == m_ListRefLeafs.m_hWnd)
1260 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1261 if(pos != NULL)
1262 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1265 break;
1267 case VK_F5:
1269 Refresh();
1271 break;
1276 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
1279 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1281 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1282 *pResult = 0;
1284 if(m_currSortCol == pNMLV->iSubItem)
1285 m_currSortDesc = !m_currSortDesc;
1286 else
1288 m_currSortCol = pNMLV->iSubItem;
1289 m_currSortDesc = false;
1292 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
1293 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
1295 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
1298 void CBrowseRefsDlg::OnDestroy()
1300 if (!m_bPickedRefSet)
1301 m_pickedRef = GetSelectedRef(true, false);
1303 int maxcol = m_ColumnManager.GetColumnCount();
1304 for (int col = 0; col < maxcol; ++col)
1305 if (m_ColumnManager.IsVisible(col))
1306 m_ColumnManager.ColumnResized(col);
1307 m_ColumnManager.WriteSettings();
1309 CResizableStandAloneDialog::OnDestroy();
1312 void CBrowseRefsDlg::OnItemChangedListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1314 LPNMLISTVIEW pNMListView = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1315 *pResult = 0;
1317 CShadowTree *item = (CShadowTree*)m_ListRefLeafs.GetItemData(pNMListView->iItem);
1318 if (item && pNMListView->uNewState == LVIS_SELECTED)
1319 m_sLastSelected = item->GetRefName();
1322 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR * /*pNMHDR*/, LRESULT *pResult)
1324 *pResult = 0;
1326 if (!m_ListRefLeafs.GetFirstSelectedItemPosition())
1327 return;
1328 EndDialog(IDOK);
1331 CString CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef, int pickRef_Kind, bool pickMultipleRefsOrRange)
1333 CBrowseRefsDlg dlg(CString(),NULL);
1335 if(initialRef.IsEmpty())
1336 initialRef = L"HEAD";
1337 dlg.m_initialRef = initialRef;
1338 dlg.m_pickRef_Kind = pickRef_Kind;
1339 dlg.m_bPickOne = !pickMultipleRefsOrRange;
1341 if(dlg.DoModal() != IDOK)
1342 return CString();
1344 return dlg.m_pickedRef;
1347 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)
1349 CString origRef;
1350 pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);
1351 CString resultRef = PickRef(false,origRef,pickRef_Kind);
1352 if(resultRef.IsEmpty())
1353 return false;
1354 if(wcsncmp(resultRef,L"refs/",5)==0)
1355 resultRef = resultRef.Mid(5);
1356 // if(wcsncmp(resultRef,L"heads/",6)==0)
1357 // resultRef = resultRef.Mid(6);
1359 //Find closest match of choice in combobox
1360 int ixFound = -1;
1361 int matchLength = 0;
1362 CString comboRefName;
1363 for(int i = 0; i < pComboBox->GetCount(); ++i)
1365 pComboBox->GetLBText(i, comboRefName);
1366 if(comboRefName.Find(L'/') < 0 && !comboRefName.IsEmpty())
1367 comboRefName.Insert(0,L"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1368 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)
1370 matchLength = comboRefName.GetLength();
1371 ixFound = i;
1374 if(ixFound >= 0)
1375 pComboBox->SetCurSel(ixFound);
1376 else
1377 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)
1379 return true;
1382 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1384 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1385 *pResult = FALSE;
1387 if(pDispInfo->item.pszText == NULL)
1388 return; //User canceled changing
1390 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1392 if(!pTree->IsFrom(L"refs/heads/"))
1394 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES, IDS_APPNAME, MB_OK | MB_ICONERROR);
1395 return;
1398 CString origName = pTree->GetRefName().Mid(11);
1400 CString newName;
1401 if(m_pListCtrlRoot != NULL)
1402 newName = m_pListCtrlRoot->GetRefName() + L'/';
1403 newName += pDispInfo->item.pszText;
1405 if(wcsncmp(newName,L"refs/heads/",11)!=0)
1407 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE, IDS_APPNAME, MB_OK | MB_ICONERROR);
1408 return;
1411 CString newNameTrunced = newName.Mid(11);
1413 CString errorMsg;
1414 if(g_Git.Run(L"git.exe branch -m \"" + origName + L"\" \"" + newNameTrunced + L"\"", &errorMsg, CP_UTF8) != 0)
1416 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1417 return;
1419 //Do as if it failed to rename. Let Refresh() do the job.
1420 //*pResult = TRUE;
1422 Refresh(newName);
1424 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1426 // AfxMessageBox(W_csPopup);
1430 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1432 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1433 *pResult = FALSE;
1435 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1437 if(!pTree->IsFrom(L"refs/heads/"))
1439 *pResult = TRUE; //Dont allow renaming any other things then branches at the moment.
1440 return;
1444 void CBrowseRefsDlg::OnEnChangeEditFilter()
1446 SetTimer(IDT_FILTER, 1000, NULL);
1449 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent)
1451 if (nIDEvent == IDT_FILTER)
1453 KillTimer(IDT_FILTER);
1454 FillListCtrlForTreeNode(m_RefTreeCtrl.GetSelectedItem());
1457 CResizableStandAloneDialog::OnTimer(nIDEvent);
1460 LRESULT CBrowseRefsDlg::OnClickedInfoIcon(WPARAM /*wParam*/, LPARAM lParam)
1462 // FIXME: x64 version would get this function called with unexpected parameters.
1463 if (!lParam)
1464 return 0;
1466 RECT * rect = (LPRECT)lParam;
1467 CPoint point;
1468 CString temp;
1469 point = CPoint(rect->left, rect->bottom);
1470 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1471 CMenu popup;
1472 if (popup.CreatePopupMenu())
1474 temp.LoadString(IDS_LOG_FILTER_REFNAME);
1475 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME), LOGFILTER_REFNAME, temp);
1477 temp.LoadString(IDS_LOG_FILTER_SUBJECT);
1478 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT), LOGFILTER_SUBJECT, temp);
1480 temp.LoadString(IDS_LOG_FILTER_AUTHORS);
1481 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS), LOGFILTER_AUTHORS, temp);
1483 temp.LoadString(IDS_LOG_FILTER_REVS);
1484 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS), LOGFILTER_REVS, temp);
1486 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1487 if (selection != 0)
1489 m_SelectedFilters ^= selection;
1490 SetFilterCueText();
1491 SetTimer(IDT_FILTER, 1000, NULL);
1494 return 0L;
1497 void CBrowseRefsDlg::SetFilterCueText()
1499 CString temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY));
1500 temp += _T(" ");
1502 if (m_SelectedFilters & LOGFILTER_REFNAME)
1503 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME));
1505 if (m_SelectedFilters & LOGFILTER_SUBJECT)
1507 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1508 temp += _T(", ");
1509 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT));
1512 if (m_SelectedFilters & LOGFILTER_AUTHORS)
1514 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1515 temp += _T(", ");
1516 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS));
1519 if (m_SelectedFilters & LOGFILTER_REVS)
1521 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1522 temp += _T(", ");
1523 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS));
1526 // to make the cue banner text appear more to the right of the edit control
1527 temp = _T(" ") + temp;
1528 m_ctrlFilter.SetCueBanner(temp.TrimRight());
1531 void CBrowseRefsDlg::OnBnClickedCurrentbranch()
1533 m_pickedRef = g_Git.GetCurrentBranch(true);
1534 m_bPickedRefSet = true;
1535 OnOK();