Fixed issue #1076: Error trying to delete remote branch named 1.0.0
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blob6e6476fcb301117f6f3e67a9d40fb72d7f53e740
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2012 - 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 "git2.h"
36 #include "UnicodeUtils.h"
37 #include "InputDlg.h"
39 void SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)
41 if (control == NULL)
42 return;
43 // set the sort arrow
44 CHeaderCtrl * pHeader = control->GetHeaderCtrl();
45 HDITEM HeaderItem = {0};
46 HeaderItem.mask = HDI_FORMAT;
47 for (int i=0; i<pHeader->GetItemCount(); ++i)
49 pHeader->GetItem(i, &HeaderItem);
50 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
51 pHeader->SetItem(i, &HeaderItem);
53 if (nColumn >= 0)
55 pHeader->GetItem(nColumn, &HeaderItem);
56 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);
57 pHeader->SetItem(nColumn, &HeaderItem);
61 class CRefLeafListCompareFunc
63 public:
64 CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){
65 m_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER);
66 if (m_bSortLogical)
67 m_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE);
70 static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
72 return ((CRefLeafListCompareFunc*)lParamSort)->Compare(lParam1,lParam2);
75 int Compare(LPARAM lParam1, LPARAM lParam2)
77 return Compare(
78 (CShadowTree*)m_pList->GetItemData((int)lParam1),
79 (CShadowTree*)m_pList->GetItemData((int)lParam2));
82 int Compare(CShadowTree* pLeft, CShadowTree* pRight)
84 int result=CompareNoDesc(pLeft,pRight);
85 if(m_desc)
86 return -result;
87 return result;
90 int CompareNoDesc(CShadowTree* pLeft, CShadowTree* pRight)
92 switch(m_col)
94 case CBrowseRefsDlg::eCol_Name: return SortStrCmp(pLeft->GetRefName(), pRight->GetRefName());
95 case CBrowseRefsDlg::eCol_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);
96 case CBrowseRefsDlg::eCol_Msg: return SortStrCmp(pLeft->m_csSubject, pRight->m_csSubject);
97 case CBrowseRefsDlg::eCol_LastAuthor: return SortStrCmp(pLeft->m_csAuthor, pRight->m_csAuthor);
98 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);
99 case CBrowseRefsDlg::eCol_Description: return SortStrCmp(pLeft->m_csDescription, pRight->m_csDescription);
101 return 0;
103 int SortStrCmp(CString left, CString right)
105 if (m_bSortLogical)
106 return StrCmpLogicalW(left, right);
107 return StrCmpI(left, right);
110 int m_col;
111 bool m_desc;
112 CListCtrl* m_pList;
113 bool m_bSortLogical;
116 // CBrowseRefsDlg dialog
118 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)
120 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)
121 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),
122 m_cmdPath(cmdPath),
123 m_currSortCol(0),
124 m_currSortDesc(false),
125 m_initialRef(L"HEAD"),
126 m_pickRef_Kind(gPickRef_All),
127 m_pListCtrlRoot(NULL),
128 m_bHasWC(true),
129 m_SelectedFilters(LOGFILTER_ALL),
130 m_bPickOne(false)
135 CBrowseRefsDlg::~CBrowseRefsDlg()
139 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)
141 CDialog::DoDataExchange(pDX);
142 DDX_Control(pDX, IDC_TREE_REF, m_RefTreeCtrl);
143 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_ListRefLeafs);
144 DDX_Control(pDX, IDC_BROWSEREFS_EDIT_FILTER, m_ctrlFilter);
148 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)
149 ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)
150 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)
151 ON_WM_CONTEXTMENU()
152 ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)
153 ON_WM_DESTROY()
154 ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)
155 ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs)
156 ON_NOTIFY(LVN_BEGINLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs)
157 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER, &CBrowseRefsDlg::OnEnChangeEditFilter)
158 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED, OnClickedInfoIcon)
159 ON_WM_TIMER()
160 END_MESSAGE_MAP()
163 // CBrowseRefsDlg message handlers
165 void CBrowseRefsDlg::OnBnClickedOk()
167 OnOK();
170 BOOL CBrowseRefsDlg::OnInitDialog()
172 CResizableStandAloneDialog::OnInitDialog();
173 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
175 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
176 m_ctrlFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
177 m_ctrlFilter.SetInfoIcon(IDI_FILTEREDIT);
178 SetFilterCueText();
180 AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);
181 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
182 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER, TOP_LEFT);
183 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER, TOP_LEFT, TOP_RIGHT);
184 AddAnchor(IDHELP, BOTTOM_RIGHT);
186 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
187 CString temp;
188 temp.LoadString(IDS_BRANCHNAME);
189 m_ListRefLeafs.InsertColumn(eCol_Name, temp, 0, 150);
190 temp.LoadString(IDS_DATELASTCOMMIT);
191 m_ListRefLeafs.InsertColumn(eCol_Date, temp, 0, 100);
192 temp.LoadString(IDS_LASTCOMMIT);
193 m_ListRefLeafs.InsertColumn(eCol_Msg, temp, 0, 300);
194 temp.LoadString(IDS_LASTAUTHOR);
195 m_ListRefLeafs.InsertColumn(eCol_LastAuthor, temp, 0, 100);
196 temp.LoadString(IDS_HASH);
197 m_ListRefLeafs.InsertColumn(eCol_Hash, temp, 0, 80);
198 temp.LoadString(IDS_DESCRIPTION);
199 m_ListRefLeafs.InsertColumn(eCol_Description, temp, 0, 80);
201 AddAnchor(IDOK,BOTTOM_RIGHT);
202 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
204 Refresh(m_initialRef);
206 EnableSaveRestore(L"BrowseRefs");
208 CString sWindowTitle;
209 GetWindowText(sWindowTitle);
210 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
212 m_bHasWC = !g_GitAdminDir.IsBareRepo(g_Git.m_CurrentDir);
214 if (m_bPickOne)
215 m_ListRefLeafs.ModifyStyle(0, LVS_SINGLESEL);
217 m_ListRefLeafs.SetFocus();
218 return FALSE;
221 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
223 int posSlash=nameLeft.Find('/');
224 CString nameSub;
225 if(posSlash<0)
227 nameSub=nameLeft;
228 nameLeft.Empty();//Nothing left
230 else
232 nameSub=nameLeft.Left(posSlash);
233 nameLeft=nameLeft.Mid(posSlash+1);
235 if(nameSub.IsEmpty())
236 return NULL;
238 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
239 return NULL;
241 CShadowTree& nextNode=m_ShadowTree[nameSub];
242 nextNode.m_csRefName=nameSub;
243 nextNode.m_pParent=this;
244 return &nextNode;
247 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)
249 if(IsLeaf())
251 if(m_csRefName.GetLength() > partialRefName.GetLength())
252 return NULL;
253 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)
255 //Match of leaf name. Try match on total name.
256 CString totalRefName = GetRefName();
257 if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)
258 return this; //Also match. Found.
261 else
263 //Not a leaf. Search all nodes.
264 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)
266 CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);
267 if(pSubtree != NULL)
268 return pSubtree; //Found
271 return NULL;//Not found
275 typedef std::map<CString,CString> MAP_STRING_STRING;
277 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf, bool pickFirstSelIfMultiSel)
279 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
280 //List ctrl selection?
281 if(pos && (pickFirstSelIfMultiSel || m_ListRefLeafs.GetSelectedCount() == 1))
283 //A leaf is selected
284 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(
285 m_ListRefLeafs.GetNextSelectedItem(pos));
286 return pTree->GetRefName();
288 else if(!onlyIfLeaf)
290 //Tree ctrl selection?
291 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
292 if(hTree!=NULL)
294 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);
295 return pTree->GetRefName();
298 return CString();//None
301 static int GetBranchDescriptionsCallback(const char *var_name, const char *value, void *data)
303 MAP_STRING_STRING *descriptions = (MAP_STRING_STRING *) data;
304 CString key = CUnicodeUtils::GetUnicode(var_name, CP_UTF8);
305 CString val = CUnicodeUtils::GetUnicode(value, CP_UTF8);
306 descriptions->insert(make_pair(key.Mid(7, key.GetLength() - 7 - 12), val)); // 7: branch., 12: .description
307 return 0;
310 MAP_STRING_STRING GetBranchDescriptions()
312 MAP_STRING_STRING descriptions;
313 git_config * config;
314 git_config_new(&config);
315 CStringA projectConfigA = CUnicodeUtils::GetMulti(g_Git.m_CurrentDir + _T("\\.git\\config"), CP_UTF8);
316 git_config_add_file_ondisk(config, projectConfigA.GetBuffer(), 3);
317 projectConfigA.ReleaseBuffer();
318 git_config_foreach_match(config, "branch\\..*\\.description", GetBranchDescriptionsCallback, &descriptions);
319 git_config_free(config);
320 return descriptions;
323 void CBrowseRefsDlg::Refresh(CString selectRef)
325 // m_RefMap.clear();
326 // g_Git.GetMapHashToFriendName(m_RefMap);
328 if(!selectRef.IsEmpty())
330 if(selectRef == "HEAD")
332 selectRef = g_Git.GetSymbolicRef(selectRef, false);
335 else
337 selectRef = GetSelectedRef(false, true);
340 m_RefTreeCtrl.DeleteAllItems();
341 m_ListRefLeafs.DeleteAllItems();
342 m_TreeRoot.m_ShadowTree.clear();
343 m_TreeRoot.m_csRefName = "refs";
344 m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);
345 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);
347 CString allRefs;
348 g_Git.Run(L"git for-each-ref --format="
349 L"%(refname)%04"
350 L"%(objectname)%04"
351 L"%(authordate:relative)%04"
352 L"%(subject)%04"
353 L"%(authorname)%04"
354 L"%(authordate:iso8601)%03",
355 &allRefs, NULL, CP_UTF8);
357 int linePos=0;
358 CString singleRef;
360 MAP_STRING_STRING refMap;
362 //First sort on ref name
363 while(!(singleRef=allRefs.Tokenize(L"\03",linePos)).IsEmpty())
365 singleRef.TrimLeft(L"\r\n");
366 int valuePos=0;
367 CString refName=singleRef.Tokenize(L"\04",valuePos);
368 if(refName.IsEmpty())
369 continue;
370 CString refRest=singleRef.Mid(valuePos);
373 //Use ref based on m_pickRef_Kind
374 if(wcsncmp(refName,L"refs/heads",10)==0 && !(m_pickRef_Kind & gPickRef_Head) )
375 continue; //Skip
376 if(wcsncmp(refName,L"refs/tags",9)==0 && !(m_pickRef_Kind & gPickRef_Tag) )
377 continue; //Skip
378 if(wcsncmp(refName,L"refs/remotes",12)==0 && !(m_pickRef_Kind & gPickRef_Remote) )
379 continue; //Skip
381 refMap[refName] = refRest; //Use
384 MAP_STRING_STRING descriptions = GetBranchDescriptions();
386 //Populate ref tree
387 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
389 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
390 CString values=iterRefMap->second;
391 values.Replace(L"\04" L"\04",L"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
393 int valuePos=0;
394 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
395 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
396 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
397 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
398 treeLeaf.m_csDate_Iso8601= values.Tokenize(L"\04",valuePos);
400 if (wcsncmp(iterRefMap->first, L"refs/heads", 10) == 0)
401 treeLeaf.m_csDescription = descriptions[treeLeaf.m_csRefName];
405 if(selectRef.IsEmpty() || !SelectRef(selectRef, false))
406 //Probably not on a branch. Select root node.
407 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);
411 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
413 if(!bExactMatch)
415 CString newRefName = GetFullRefName(refName);
416 if(!newRefName.IsEmpty())
417 refName = newRefName;
418 //else refName is not a valid ref. Try to select as good as possible.
420 if(_wcsnicmp(refName, L"refs/", 5) != 0)
421 return false; // Not a ref name
423 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
424 if(treeLeafHead.m_hTree != NULL)
426 //Not a leaf. Select tree node and return
427 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
428 return true;
431 if(treeLeafHead.m_pParent==NULL)
432 return false; //Weird... should not occur.
434 //This is the current head.
435 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
437 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
439 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
440 if(pCurrShadowTree == &treeLeafHead)
442 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
443 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
447 return true;
450 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
452 if(pTreePos==NULL)
454 if(_wcsnicmp(refName, L"refs/", 5) == 0)
455 refName=refName.Mid(5);
456 pTreePos=&m_TreeRoot;
458 if(refName.IsEmpty())
459 return *pTreePos;//Found leaf
461 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
462 if(pNextTree==NULL)
464 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
465 ASSERT(!bCreateIfNotExist);
466 return *pTreePos;
469 if(!refName.IsEmpty())
471 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
472 //Leafs are for the list control.
473 if(pNextTree->m_hTree==NULL)
475 //New tree. Create node in control.
476 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
477 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
481 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
485 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
487 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
488 *pResult = 0;
490 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
493 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
495 m_ListRefLeafs.DeleteAllItems();
497 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
498 if(pTree==NULL)
500 ASSERT(FALSE);
501 return;
503 FillListCtrlForShadowTree(pTree,L"",true);
506 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
508 if(pTree->IsLeaf())
510 CString filter;
511 m_ctrlFilter.GetWindowText(filter);
512 filter.MakeLower();
513 CString ref = refNamePrefix + pTree->m_csRefName;
514 if (!(pTree->m_csRefName.IsEmpty() || pTree->m_csRefName == "refs" && pTree->m_pParent == NULL) && IsMatchFilter(pTree, ref, filter))
516 int indexItem = m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(), L"");
518 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
519 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, ref);
520 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);
521 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
522 m_ListRefLeafs.SetItemText(indexItem,eCol_LastAuthor, pTree->m_csAuthor);
523 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
524 int pos = 0;
525 m_ListRefLeafs.SetItemText(indexItem,eCol_Description, pTree->m_csDescription.Tokenize(_T("\n"), pos));
528 else
531 CString csThisName;
532 if(!isFirstLevel)
533 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
534 else
535 m_pListCtrlRoot = pTree;
536 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
538 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
541 if (isFirstLevel)
543 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
544 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
546 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
550 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree* pTree, const CString &ref, const CString &filter)
552 if (m_SelectedFilters & LOGFILTER_REFNAME)
554 CString msg = ref;
555 msg = msg.MakeLower();
557 if (msg.Find(filter) >= 0)
558 return true;
561 if (m_SelectedFilters & LOGFILTER_SUBJECT)
563 CString msg = pTree->m_csSubject;
564 msg = msg.MakeLower();
566 if (msg.Find(filter) >= 0)
567 return true;
570 if (m_SelectedFilters & LOGFILTER_AUTHORS)
572 CString msg = pTree->m_csAuthor;
573 msg = msg.MakeLower();
575 if (msg.Find(filter) >= 0)
576 return true;
579 if (m_SelectedFilters & LOGFILTER_REVS)
581 CString msg = pTree->m_csRefHash;
582 msg = msg.MakeLower();
584 if (msg.Find(filter) >= 0)
585 return true;
587 return false;
590 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree& leafs)
592 ASSERT(!leafs.empty());
594 CString csMessage;
595 UINT mbIcon=MB_ICONQUESTION;
597 bool bIsRemoteBranch = false;
598 bool bIsBranch = false;
599 if (leafs[0]->IsFrom(L"refs/remotes")) {bIsBranch = true; bIsRemoteBranch = true;}
600 else if (leafs[0]->IsFrom(L"refs/heads")) {bIsBranch = true;}
602 if(bIsBranch)
604 if(leafs.size() == 1)
606 CString branchToDelete = leafs[0]->GetRefName().Mid(bIsRemoteBranch ? 13 : 11);
607 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, branchToDelete);
609 //Check if branch is fully merged in HEAD
610 CGitHash branchHash = g_Git.GetHash(leafs[0]->GetRefName());
611 CGitHash commonAncestor;
612 CString commonAncestorstr;
613 CString cmd;
614 cmd.Format(L"git.exe merge-base HEAD %s", leafs[0]->GetRefName());
615 g_Git.Run(cmd, &commonAncestorstr, NULL, CP_UTF8);
617 commonAncestor=commonAncestorstr;
619 if(commonAncestor != branchHash)
621 csMessage += L"\r\n\r\n";
622 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED));
623 mbIcon = MB_ICONWARNING;
626 if(bIsRemoteBranch)
628 csMessage += L"\r\n\r\n";
629 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
630 mbIcon = MB_ICONWARNING;
633 else
635 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
637 csMessage += L"\r\n\r\n";
638 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK));
639 mbIcon = MB_ICONWARNING;
641 if(bIsRemoteBranch)
643 csMessage += L"\r\n\r\n";
644 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
645 mbIcon = MB_ICONWARNING;
650 else if(leafs[0]->IsFrom(L"refs/tags"))
652 if(leafs.size() == 1)
654 CString tagToDelete = leafs[0]->GetRefName().Mid(10);
655 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, tagToDelete);
657 else
659 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
663 return CMessageBox::Show(m_hWnd, csMessage, _T("TortoiseGit"), MB_YESNO | mbIcon) == IDYES;
667 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree& leafs, bool bForce)
669 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
670 if(!DoDeleteRef((*i)->GetRefName(), bForce))
671 return false;
672 return true;
675 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)
677 bool bIsRemoteBranch = false;
678 bool bIsBranch = false;
679 if (wcsncmp(completeRefName, L"refs/remotes",12)==0) {bIsBranch = true; bIsRemoteBranch = true;}
680 else if (wcsncmp(completeRefName, L"refs/heads",10)==0) {bIsBranch = true;}
682 if(bIsBranch)
684 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);
685 CString cmd;
686 if(bIsRemoteBranch)
688 int slash = branchToDelete.Find(L'/');
689 if(slash < 0)
690 return false;
691 CString remoteName = branchToDelete.Left(slash);
692 CString remoteBranchToDelete = branchToDelete.Mid(slash + 1);
694 if(CAppUtils::IsSSHPutty())
696 CAppUtils::LaunchPAgent(NULL, &remoteName);
699 cmd.Format(L"git.exe push \"%s\" :refs/heads/%s", remoteName, remoteBranchToDelete);
701 else
702 cmd.Format(L"git.exe branch -%c -- %s",bForce?L'D':L'd',branchToDelete);
703 CString errorMsg;
704 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
706 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
707 return false;
710 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
712 CString tagToDelete = completeRefName.Mid(10);
713 CString cmd;
714 cmd.Format(L"git.exe tag -d -- %s",tagToDelete);
715 CString errorMsg;
716 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
718 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
719 return false;
722 return true;
725 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
727 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
728 if(pLeaf == NULL)
729 return CString();
730 return pLeaf->GetRefName();
734 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
736 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
737 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
740 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
742 CPoint clientPoint=point;
743 m_RefTreeCtrl.ScreenToClient(&clientPoint);
745 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
746 if(hTreeItem!=NULL)
747 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
749 VectorPShadowTree tree;
750 ShowContextMenu(point,hTreeItem,tree);
754 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
756 std::vector<CShadowTree*> selectedLeafs;
757 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
758 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
759 while(pos)
761 selectedLeafs.push_back(
762 (CShadowTree*)m_ListRefLeafs.GetItemData(
763 m_ListRefLeafs.GetNextSelectedItem(pos)));
766 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
769 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
771 CIconMenu popupMenu;
772 popupMenu.CreatePopupMenu();
774 bool bAddSeparator = false;
775 CString remoteName;
777 if(selectedLeafs.size()==1)
779 bAddSeparator = true;
781 bool bShowReflogOption = false;
782 bool bShowFetchOption = false;
783 bool bShowSwitchOption = false;
784 bool bShowRenameOption = false;
785 bool bShowCreateBranchOption = false;
786 bool bShowEditBranchDescriptionOption = false;
788 CString fetchFromCmd;
790 if(selectedLeafs[0]->IsFrom(L"refs/heads"))
792 bShowReflogOption = true;
793 bShowSwitchOption = true;
794 bShowRenameOption = true;
795 bShowEditBranchDescriptionOption = true;
797 else if(selectedLeafs[0]->IsFrom(L"refs/remotes"))
799 bShowReflogOption = true;
800 bShowFetchOption = true;
801 bShowCreateBranchOption = true;
803 int dummy = 0;//Needed for tokenize
804 remoteName = selectedLeafs[0]->GetRefName();
805 remoteName = remoteName.Mid(13);
806 remoteName = remoteName.Tokenize(L"/", dummy);
807 fetchFromCmd.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
809 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))
813 CString temp;
814 temp.LoadString(IDS_MENULOG);
815 popupMenu.AppendMenuIcon(eCmd_ViewLog, temp, IDI_LOG);
816 popupMenu.AppendMenuIcon(eCmd_RepoBrowser, IDS_LOG_BROWSEREPO, IDI_REPOBROWSE);
817 if(bShowReflogOption)
819 temp.LoadString(IDS_MENUREFLOG);
820 popupMenu.AppendMenuIcon(eCmd_ShowReflog, temp, IDI_LOG);
823 popupMenu.AppendMenu(MF_SEPARATOR);
824 bAddSeparator = false;
826 if(bShowFetchOption)
828 bAddSeparator = true;
829 popupMenu.AppendMenuIcon(eCmd_Fetch, fetchFromCmd, IDI_PULL);
832 if(bAddSeparator)
833 popupMenu.AppendMenu(MF_SEPARATOR);
835 bAddSeparator = false;
836 if (m_bHasWC)
838 popupMenu.AppendMenuIcon(eCmd_Switch, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS)), IDI_SWITCH);
839 popupMenu.AppendMenu(MF_SEPARATOR);
842 if(bShowCreateBranchOption)
844 bAddSeparator = true;
845 temp.LoadString(IDS_MENUBRANCH);
846 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
849 if (bShowEditBranchDescriptionOption)
851 bAddSeparator = true;
852 popupMenu.AppendMenuIcon(eCmd_EditBranchDescription, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION)), IDI_RENAME);
854 if(bShowRenameOption)
856 bAddSeparator = true;
857 popupMenu.AppendMenuIcon(eCmd_Rename, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME)), IDI_RENAME);
860 else if(selectedLeafs.size() == 2)
862 bAddSeparator = true;
863 popupMenu.AppendMenuIcon(eCmd_Diff, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS)), IDI_DIFF);
866 if(!selectedLeafs.empty())
868 if(AreAllFrom(selectedLeafs, L"refs/remotes/"))
870 if(bAddSeparator)
871 popupMenu.AppendMenu(MF_SEPARATOR);
872 CString menuItemName;
873 if(selectedLeafs.size() == 1)
874 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH);
875 else
876 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES, selectedLeafs.size());
878 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteBranch, menuItemName, IDI_DELETE);
879 bAddSeparator = true;
881 else if(AreAllFrom(selectedLeafs, L"refs/heads/"))
883 if(bAddSeparator)
884 popupMenu.AppendMenu(MF_SEPARATOR);
885 CString menuItemName;
886 if(selectedLeafs.size() == 1)
887 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH);
888 else
889 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES, selectedLeafs.size());
891 popupMenu.AppendMenuIcon(eCmd_DeleteBranch, menuItemName, IDI_DELETE);
892 bAddSeparator = true;
894 else if(AreAllFrom(selectedLeafs, L"refs/tags/"))
896 if(bAddSeparator)
897 popupMenu.AppendMenu(MF_SEPARATOR);
898 CString menuItemName;
899 if(selectedLeafs.size() == 1)
900 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETETAG);
901 else
902 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETETAGS, selectedLeafs.size());
904 popupMenu.AppendMenuIcon(eCmd_DeleteTag, menuItemName, IDI_DELETE);
905 bAddSeparator = true;
910 if(hTreePos!=NULL && selectedLeafs.empty())
912 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);
913 if(pTree->IsFrom(L"refs/remotes"))
915 if(bAddSeparator)
916 popupMenu.AppendMenu(MF_SEPARATOR);
917 popupMenu.AppendMenuIcon(eCmd_ManageRemotes, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES)), IDI_SETTINGS);
918 bAddSeparator = true;
919 if(selectedLeafs.empty())
921 int dummy = 0;//Needed for tokenize
922 remoteName = pTree->GetRefName();
923 remoteName = remoteName.Mid(13);
924 remoteName = remoteName.Tokenize(L"/", dummy);
925 if(!remoteName.IsEmpty())
927 CString temp;
928 temp.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
929 popupMenu.AppendMenuIcon(eCmd_Fetch, temp, IDI_PULL);
931 temp.LoadString(IDS_DELETEREMOTETAG);
932 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteTag, temp, IDI_DELETE);
936 if(pTree->IsFrom(L"refs/heads"))
938 if(bAddSeparator)
939 popupMenu.AppendMenu(MF_SEPARATOR);
940 CString temp;
941 temp.LoadString(IDS_MENUBRANCH);
942 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
944 if(pTree->IsFrom(L"refs/tags"))
946 if(bAddSeparator)
947 popupMenu.AppendMenu(MF_SEPARATOR);
948 CString temp;
949 temp.LoadString(IDS_MENUTAG);
950 popupMenu.AppendMenuIcon(eCmd_CreateTag, temp, IDI_TAG);
955 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
956 switch(cmd)
958 case eCmd_ViewLog:
960 CLogDlg dlg;
961 dlg.SetStartRef(selectedLeafs[0]->GetRefName());
962 dlg.DoModal();
964 break;
965 case eCmd_RepoBrowser:
966 CAppUtils::RunTortoiseProc(_T("/command:repobrowser /path:\"") + g_Git.m_CurrentDir + _T("\" /rev:") + selectedLeafs[0]->GetRefName());
967 break;
968 case eCmd_DeleteBranch:
969 case eCmd_DeleteRemoteBranch:
971 if(ConfirmDeleteRef(selectedLeafs))
972 DoDeleteRefs(selectedLeafs, true);
973 Refresh();
975 break;
976 case eCmd_DeleteTag:
978 if(ConfirmDeleteRef(selectedLeafs))
979 DoDeleteRefs(selectedLeafs, true);
980 Refresh();
982 break;
983 case eCmd_ShowReflog:
985 CRefLogDlg refLogDlg(this);
986 refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();
987 refLogDlg.DoModal();
989 break;
990 case eCmd_Fetch:
992 CAppUtils::Fetch(remoteName);
993 Refresh();
995 break;
996 case eCmd_DeleteRemoteTag:
998 CDeleteRemoteTagDlg deleteRemoteTagDlg;
999 deleteRemoteTagDlg.m_sRemote = remoteName;
1000 deleteRemoteTagDlg.DoModal();
1002 break;
1003 case eCmd_Switch:
1005 CAppUtils::Switch(selectedLeafs[0]->GetRefName());
1007 break;
1008 case eCmd_Rename:
1010 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1011 if(pos != NULL)
1012 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1014 break;
1015 case eCmd_AddRemote:
1017 CAddRemoteDlg(this).DoModal();
1018 Refresh();
1020 break;
1021 case eCmd_ManageRemotes:
1023 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS)), new CSettingGitRemote(g_Git.m_CurrentDir), this).DoModal();
1024 // CSettingGitRemote W_Remotes(m_cmdPath);
1025 // W_Remotes.DoModal();
1026 Refresh();
1028 break;
1029 case eCmd_CreateBranch:
1031 CString *commitHash = NULL;
1032 if (selectedLeafs.size() == 1)
1033 commitHash = &(selectedLeafs[0]->m_csRefHash);
1034 CAppUtils::CreateBranchTag(false, commitHash);
1035 Refresh();
1037 break;
1038 case eCmd_CreateTag:
1040 CAppUtils::CreateBranchTag(true);
1041 Refresh();
1043 break;
1044 case eCmd_Diff:
1046 CFileDiffDlg dlg;
1047 dlg.SetDiff(
1048 NULL,
1049 selectedLeafs[0]->m_csRefHash,
1050 selectedLeafs[1]->m_csRefHash);
1051 dlg.DoModal();
1053 break;
1054 case eCmd_EditBranchDescription:
1056 CInputDlg dlg;
1057 dlg.m_sHintText = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1058 dlg.m_sInputText = selectedLeafs[0]->m_csDescription;
1059 dlg.m_sTitle = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1060 dlg.m_bUseLogWidth = true;
1061 if(dlg.DoModal() == IDOK)
1063 CString key;
1064 key.Format(_T("branch.%s.description"), selectedLeafs[0]->m_csRefName);
1065 dlg.m_sInputText.Replace(_T("\r"), _T(""));
1066 dlg.m_sInputText.Trim();
1067 if (dlg.m_sInputText.IsEmpty())
1068 g_Git.UnsetConfigValue(key);
1069 else
1070 g_Git.SetConfigValue(key, dlg.m_sInputText);
1071 Refresh();
1074 break;
1078 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree& leafs, const wchar_t* from)
1080 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
1081 if(!(*i)->IsFrom(from))
1082 return false;
1083 return true;
1086 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
1088 if (pMsg->message == WM_KEYDOWN)
1090 switch (pMsg->wParam)
1092 /* case VK_RETURN:
1094 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1096 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1098 PostMessage(WM_COMMAND, IDOK);
1100 return TRUE;
1103 break;
1104 */ case VK_F2:
1106 if(pMsg->hwnd == m_ListRefLeafs.m_hWnd)
1108 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1109 if(pos != NULL)
1110 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1113 break;
1115 case VK_F5:
1117 Refresh();
1119 break;
1124 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
1127 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1129 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1130 *pResult = 0;
1132 if(m_currSortCol == pNMLV->iSubItem)
1133 m_currSortDesc = !m_currSortDesc;
1134 else
1136 m_currSortCol = pNMLV->iSubItem;
1137 m_currSortDesc = false;
1140 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
1141 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
1143 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
1146 void CBrowseRefsDlg::OnDestroy()
1148 m_pickedRef = GetSelectedRef(true, false);
1150 CResizableStandAloneDialog::OnDestroy();
1153 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1155 UNREFERENCED_PARAMETER(pNMHDR);
1156 *pResult = 0;
1158 EndDialog(IDOK);
1161 CString CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef, int pickRef_Kind)
1163 CBrowseRefsDlg dlg(CString(),NULL);
1165 if(initialRef.IsEmpty())
1166 initialRef = L"HEAD";
1167 dlg.m_initialRef = initialRef;
1168 dlg.m_pickRef_Kind = pickRef_Kind;
1169 dlg.m_bPickOne = true;
1171 if(dlg.DoModal() != IDOK)
1172 return CString();
1174 return dlg.m_pickedRef;
1177 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)
1179 CString origRef;
1180 pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);
1181 CString resultRef = PickRef(false,origRef,pickRef_Kind);
1182 if(resultRef.IsEmpty())
1183 return false;
1184 if(wcsncmp(resultRef,L"refs/",5)==0)
1185 resultRef = resultRef.Mid(5);
1186 // if(wcsncmp(resultRef,L"heads/",6)==0)
1187 // resultRef = resultRef.Mid(6);
1189 //Find closest match of choice in combobox
1190 int ixFound = -1;
1191 int matchLength = 0;
1192 CString comboRefName;
1193 for(int i = 0; i < pComboBox->GetCount(); ++i)
1195 pComboBox->GetLBText(i, comboRefName);
1196 if(comboRefName.Find(L'/') < 0 && !comboRefName.IsEmpty())
1197 comboRefName.Insert(0,L"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1198 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)
1200 matchLength = comboRefName.GetLength();
1201 ixFound = i;
1204 if(ixFound >= 0)
1205 pComboBox->SetCurSel(ixFound);
1206 else
1207 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)
1209 return true;
1212 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1214 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1215 *pResult = FALSE;
1217 if(pDispInfo->item.pszText == NULL)
1218 return; //User canceled changing
1220 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1222 if(!pTree->IsFrom(L"refs/heads"))
1224 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES, IDS_APPNAME, MB_OK | MB_ICONERROR);
1225 return;
1228 CString origName = pTree->GetRefName().Mid(11);
1230 CString newName;
1231 if(m_pListCtrlRoot != NULL)
1232 newName = m_pListCtrlRoot->GetRefName() + L'/';
1233 newName += pDispInfo->item.pszText;
1235 if(wcsncmp(newName,L"refs/heads/",11)!=0)
1237 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE, IDS_APPNAME, MB_OK | MB_ICONERROR);
1238 return;
1241 CString newNameTrunced = newName.Mid(11);
1243 CString errorMsg;
1244 if(g_Git.Run(L"git branch -m \"" + origName + L"\" \"" + newNameTrunced + L"\"", &errorMsg, CP_UTF8) != 0)
1246 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1247 return;
1249 //Do as if it failed to rename. Let Refresh() do the job.
1250 //*pResult = TRUE;
1252 Refresh(newName);
1254 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1256 // AfxMessageBox(W_csPopup);
1260 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1262 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1263 *pResult = FALSE;
1265 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1267 if(!pTree->IsFrom(L"refs/heads"))
1269 *pResult = TRUE; //Dont allow renaming any other things then branches at the moment.
1270 return;
1274 void CBrowseRefsDlg::OnEnChangeEditFilter()
1276 SetTimer(IDT_FILTER, 1000, NULL);
1279 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent)
1281 if (nIDEvent == IDT_FILTER)
1283 KillTimer(IDT_FILTER);
1284 FillListCtrlForTreeNode(m_RefTreeCtrl.GetSelectedItem());
1287 CResizableStandAloneDialog::OnTimer(nIDEvent);
1290 LRESULT CBrowseRefsDlg::OnClickedInfoIcon(WPARAM /*wParam*/, LPARAM lParam)
1292 // FIXME: x64 version would get this function called with unexpected parameters.
1293 if (!lParam)
1294 return 0;
1296 RECT * rect = (LPRECT)lParam;
1297 CPoint point;
1298 CString temp;
1299 point = CPoint(rect->left, rect->bottom);
1300 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1301 CMenu popup;
1302 if (popup.CreatePopupMenu())
1304 temp.LoadString(IDS_LOG_FILTER_REFNAME);
1305 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME), LOGFILTER_REFNAME, temp);
1307 temp.LoadString(IDS_LOG_FILTER_SUBJECT);
1308 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT), LOGFILTER_SUBJECT, temp);
1310 temp.LoadString(IDS_LOG_FILTER_AUTHORS);
1311 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS), LOGFILTER_AUTHORS, temp);
1313 temp.LoadString(IDS_LOG_FILTER_REVS);
1314 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS), LOGFILTER_REVS, temp);
1316 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1317 if (selection != 0)
1319 m_SelectedFilters ^= selection;
1320 SetFilterCueText();
1321 SetTimer(IDT_FILTER, 1000, NULL);
1324 return 0L;
1327 void CBrowseRefsDlg::SetFilterCueText()
1329 CString temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY));
1330 temp += _T(" ");
1332 if (m_SelectedFilters & LOGFILTER_REFNAME)
1333 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME));
1335 if (m_SelectedFilters & LOGFILTER_SUBJECT)
1337 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1338 temp += _T(", ");
1339 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT));
1342 if (m_SelectedFilters & LOGFILTER_AUTHORS)
1344 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1345 temp += _T(", ");
1346 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS));
1349 if (m_SelectedFilters & LOGFILTER_REVS)
1351 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1352 temp += _T(", ");
1353 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS));
1356 // to make the cue banner text appear more to the right of the edit control
1357 temp = _T(" ") + temp;
1358 m_ctrlFilter.SetCueBanner(temp.TrimRight());