Fixed issue #1405: Cannot show branch description in Browse Reference Dialog with...
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blob988d46ce40e3bfd1129e43b30a4ba10378b76fed
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 CString dotGitPath;
316 g_GitAdminDir.GetAdminDirPath(g_Git.m_CurrentDir, dotGitPath);
317 CStringA projectConfigA = CUnicodeUtils::GetMulti(dotGitPath + _T("config"), CP_UTF8);
318 git_config_add_file_ondisk(config, projectConfigA.GetBuffer(), 3);
319 projectConfigA.ReleaseBuffer();
320 git_config_foreach_match(config, "branch\\..*\\.description", GetBranchDescriptionsCallback, &descriptions);
321 git_config_free(config);
322 return descriptions;
325 void CBrowseRefsDlg::Refresh(CString selectRef)
327 // m_RefMap.clear();
328 // g_Git.GetMapHashToFriendName(m_RefMap);
330 if(!selectRef.IsEmpty())
332 if(selectRef == "HEAD")
334 selectRef = g_Git.GetSymbolicRef(selectRef, false);
337 else
339 selectRef = GetSelectedRef(false, true);
342 m_RefTreeCtrl.DeleteAllItems();
343 m_ListRefLeafs.DeleteAllItems();
344 m_TreeRoot.m_ShadowTree.clear();
345 m_TreeRoot.m_csRefName = "refs";
346 m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);
347 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);
349 CString allRefs;
350 g_Git.Run(L"git for-each-ref --format="
351 L"%(refname)%04"
352 L"%(objectname)%04"
353 L"%(authordate:relative)%04"
354 L"%(subject)%04"
355 L"%(authorname)%04"
356 L"%(authordate:iso8601)%03",
357 &allRefs, NULL, CP_UTF8);
359 int linePos=0;
360 CString singleRef;
362 MAP_STRING_STRING refMap;
364 //First sort on ref name
365 while(!(singleRef=allRefs.Tokenize(L"\03",linePos)).IsEmpty())
367 singleRef.TrimLeft(L"\r\n");
368 int valuePos=0;
369 CString refName=singleRef.Tokenize(L"\04",valuePos);
370 if(refName.IsEmpty())
371 continue;
372 CString refRest=singleRef.Mid(valuePos);
375 //Use ref based on m_pickRef_Kind
376 if(wcsncmp(refName,L"refs/heads",10)==0 && !(m_pickRef_Kind & gPickRef_Head) )
377 continue; //Skip
378 if(wcsncmp(refName,L"refs/tags",9)==0 && !(m_pickRef_Kind & gPickRef_Tag) )
379 continue; //Skip
380 if(wcsncmp(refName,L"refs/remotes",12)==0 && !(m_pickRef_Kind & gPickRef_Remote) )
381 continue; //Skip
383 refMap[refName] = refRest; //Use
386 MAP_STRING_STRING descriptions = GetBranchDescriptions();
388 //Populate ref tree
389 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
391 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
392 CString values=iterRefMap->second;
393 values.Replace(L"\04" L"\04",L"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
395 int valuePos=0;
396 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
397 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
398 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
399 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
400 treeLeaf.m_csDate_Iso8601= values.Tokenize(L"\04",valuePos);
402 if (wcsncmp(iterRefMap->first, L"refs/heads", 10) == 0)
403 treeLeaf.m_csDescription = descriptions[treeLeaf.m_csRefName];
407 if(selectRef.IsEmpty() || !SelectRef(selectRef, false))
408 //Probably not on a branch. Select root node.
409 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);
413 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
415 if(!bExactMatch)
417 CString newRefName = GetFullRefName(refName);
418 if(!newRefName.IsEmpty())
419 refName = newRefName;
420 //else refName is not a valid ref. Try to select as good as possible.
422 if(_wcsnicmp(refName, L"refs/", 5) != 0)
423 return false; // Not a ref name
425 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
426 if(treeLeafHead.m_hTree != NULL)
428 //Not a leaf. Select tree node and return
429 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
430 return true;
433 if(treeLeafHead.m_pParent==NULL)
434 return false; //Weird... should not occur.
436 //This is the current head.
437 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
439 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
441 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
442 if(pCurrShadowTree == &treeLeafHead)
444 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
445 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
449 return true;
452 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
454 if(pTreePos==NULL)
456 if(_wcsnicmp(refName, L"refs/", 5) == 0)
457 refName=refName.Mid(5);
458 pTreePos=&m_TreeRoot;
460 if(refName.IsEmpty())
461 return *pTreePos;//Found leaf
463 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
464 if(pNextTree==NULL)
466 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
467 ASSERT(!bCreateIfNotExist);
468 return *pTreePos;
471 if(!refName.IsEmpty())
473 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
474 //Leafs are for the list control.
475 if(pNextTree->m_hTree==NULL)
477 //New tree. Create node in control.
478 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
479 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
483 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
487 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
489 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
490 *pResult = 0;
492 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
495 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
497 m_ListRefLeafs.DeleteAllItems();
499 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
500 if(pTree==NULL)
502 ASSERT(FALSE);
503 return;
505 FillListCtrlForShadowTree(pTree,L"",true);
508 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
510 if(pTree->IsLeaf())
512 CString filter;
513 m_ctrlFilter.GetWindowText(filter);
514 filter.MakeLower();
515 CString ref = refNamePrefix + pTree->m_csRefName;
516 if (!(pTree->m_csRefName.IsEmpty() || pTree->m_csRefName == "refs" && pTree->m_pParent == NULL) && IsMatchFilter(pTree, ref, filter))
518 int indexItem = m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(), L"");
520 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
521 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, ref);
522 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);
523 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
524 m_ListRefLeafs.SetItemText(indexItem,eCol_LastAuthor, pTree->m_csAuthor);
525 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
526 int pos = 0;
527 m_ListRefLeafs.SetItemText(indexItem,eCol_Description, pTree->m_csDescription.Tokenize(_T("\n"), pos));
530 else
533 CString csThisName;
534 if(!isFirstLevel)
535 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
536 else
537 m_pListCtrlRoot = pTree;
538 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
540 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
543 if (isFirstLevel)
545 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
546 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
548 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
552 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree* pTree, const CString &ref, const CString &filter)
554 if (m_SelectedFilters & LOGFILTER_REFNAME)
556 CString msg = ref;
557 msg = msg.MakeLower();
559 if (msg.Find(filter) >= 0)
560 return true;
563 if (m_SelectedFilters & LOGFILTER_SUBJECT)
565 CString msg = pTree->m_csSubject;
566 msg = msg.MakeLower();
568 if (msg.Find(filter) >= 0)
569 return true;
572 if (m_SelectedFilters & LOGFILTER_AUTHORS)
574 CString msg = pTree->m_csAuthor;
575 msg = msg.MakeLower();
577 if (msg.Find(filter) >= 0)
578 return true;
581 if (m_SelectedFilters & LOGFILTER_REVS)
583 CString msg = pTree->m_csRefHash;
584 msg = msg.MakeLower();
586 if (msg.Find(filter) >= 0)
587 return true;
589 return false;
592 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree& leafs)
594 ASSERT(!leafs.empty());
596 CString csMessage;
597 UINT mbIcon=MB_ICONQUESTION;
599 bool bIsRemoteBranch = false;
600 bool bIsBranch = false;
601 if (leafs[0]->IsFrom(L"refs/remotes")) {bIsBranch = true; bIsRemoteBranch = true;}
602 else if (leafs[0]->IsFrom(L"refs/heads")) {bIsBranch = true;}
604 if(bIsBranch)
606 if(leafs.size() == 1)
608 CString branchToDelete = leafs[0]->GetRefName().Mid(bIsRemoteBranch ? 13 : 11);
609 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, branchToDelete);
611 //Check if branch is fully merged in HEAD
612 if (!g_Git.IsFastForward(leafs[0]->GetRefName(), _T("HEAD")))
614 csMessage += L"\r\n\r\n";
615 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED));
616 mbIcon = MB_ICONWARNING;
619 if(bIsRemoteBranch)
621 csMessage += L"\r\n\r\n";
622 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
623 mbIcon = MB_ICONWARNING;
626 else
628 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
630 csMessage += L"\r\n\r\n";
631 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK));
632 mbIcon = MB_ICONWARNING;
634 if(bIsRemoteBranch)
636 csMessage += L"\r\n\r\n";
637 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
638 mbIcon = MB_ICONWARNING;
643 else if(leafs[0]->IsFrom(L"refs/tags"))
645 if(leafs.size() == 1)
647 CString tagToDelete = leafs[0]->GetRefName().Mid(10);
648 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, tagToDelete);
650 else
652 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
656 return CMessageBox::Show(m_hWnd, csMessage, _T("TortoiseGit"), MB_YESNO | mbIcon) == IDYES;
660 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree& leafs, bool bForce)
662 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
663 if(!DoDeleteRef((*i)->GetRefName(), bForce))
664 return false;
665 return true;
668 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)
670 bool bIsRemoteBranch = false;
671 bool bIsBranch = false;
672 if (wcsncmp(completeRefName, L"refs/remotes",12)==0) {bIsBranch = true; bIsRemoteBranch = true;}
673 else if (wcsncmp(completeRefName, L"refs/heads",10)==0) {bIsBranch = true;}
675 if(bIsBranch)
677 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);
678 CString cmd;
679 if(bIsRemoteBranch)
681 int slash = branchToDelete.Find(L'/');
682 if(slash < 0)
683 return false;
684 CString remoteName = branchToDelete.Left(slash);
685 CString remoteBranchToDelete = branchToDelete.Mid(slash + 1);
687 if(CAppUtils::IsSSHPutty())
689 CAppUtils::LaunchPAgent(NULL, &remoteName);
692 cmd.Format(L"git.exe push \"%s\" :refs/heads/%s", remoteName, remoteBranchToDelete);
694 else
695 cmd.Format(L"git.exe branch -%c -- %s",bForce?L'D':L'd',branchToDelete);
696 CString errorMsg;
697 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
699 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
700 return false;
703 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
705 CString tagToDelete = completeRefName.Mid(10);
706 CString cmd;
707 cmd.Format(L"git.exe tag -d -- %s",tagToDelete);
708 CString errorMsg;
709 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
711 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
712 return false;
715 return true;
718 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
720 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
721 if(pLeaf == NULL)
722 return CString();
723 return pLeaf->GetRefName();
727 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
729 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
730 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
733 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
735 CPoint clientPoint=point;
736 m_RefTreeCtrl.ScreenToClient(&clientPoint);
738 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
739 if(hTreeItem!=NULL)
740 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
742 VectorPShadowTree tree;
743 ShowContextMenu(point,hTreeItem,tree);
747 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
749 std::vector<CShadowTree*> selectedLeafs;
750 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
751 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
752 while(pos)
754 selectedLeafs.push_back(
755 (CShadowTree*)m_ListRefLeafs.GetItemData(
756 m_ListRefLeafs.GetNextSelectedItem(pos)));
759 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
762 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
764 CIconMenu popupMenu;
765 popupMenu.CreatePopupMenu();
767 bool bAddSeparator = false;
768 CString remoteName;
770 if(selectedLeafs.size()==1)
772 bAddSeparator = true;
774 bool bShowReflogOption = false;
775 bool bShowFetchOption = false;
776 bool bShowSwitchOption = false;
777 bool bShowRenameOption = false;
778 bool bShowCreateBranchOption = false;
779 bool bShowEditBranchDescriptionOption = false;
781 CString fetchFromCmd;
783 if(selectedLeafs[0]->IsFrom(L"refs/heads"))
785 bShowReflogOption = true;
786 bShowSwitchOption = true;
787 bShowRenameOption = true;
788 bShowEditBranchDescriptionOption = true;
790 else if(selectedLeafs[0]->IsFrom(L"refs/remotes"))
792 bShowReflogOption = true;
793 bShowFetchOption = true;
794 bShowCreateBranchOption = true;
796 int dummy = 0;//Needed for tokenize
797 remoteName = selectedLeafs[0]->GetRefName();
798 remoteName = remoteName.Mid(13);
799 remoteName = remoteName.Tokenize(L"/", dummy);
800 fetchFromCmd.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
802 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))
806 CString temp;
807 temp.LoadString(IDS_MENULOG);
808 popupMenu.AppendMenuIcon(eCmd_ViewLog, temp, IDI_LOG);
809 popupMenu.AppendMenuIcon(eCmd_RepoBrowser, IDS_LOG_BROWSEREPO, IDI_REPOBROWSE);
810 if(bShowReflogOption)
812 temp.LoadString(IDS_MENUREFLOG);
813 popupMenu.AppendMenuIcon(eCmd_ShowReflog, temp, IDI_LOG);
816 popupMenu.AppendMenu(MF_SEPARATOR);
817 bAddSeparator = false;
819 if(bShowFetchOption)
821 bAddSeparator = true;
822 popupMenu.AppendMenuIcon(eCmd_Fetch, fetchFromCmd, IDI_PULL);
825 if(bAddSeparator)
826 popupMenu.AppendMenu(MF_SEPARATOR);
828 bAddSeparator = false;
829 if (m_bHasWC)
831 popupMenu.AppendMenuIcon(eCmd_Switch, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS)), IDI_SWITCH);
832 popupMenu.AppendMenu(MF_SEPARATOR);
835 if(bShowCreateBranchOption)
837 bAddSeparator = true;
838 temp.LoadString(IDS_MENUBRANCH);
839 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
842 if (bShowEditBranchDescriptionOption)
844 bAddSeparator = true;
845 popupMenu.AppendMenuIcon(eCmd_EditBranchDescription, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION)), IDI_RENAME);
847 if(bShowRenameOption)
849 bAddSeparator = true;
850 popupMenu.AppendMenuIcon(eCmd_Rename, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME)), IDI_RENAME);
853 else if(selectedLeafs.size() == 2)
855 bAddSeparator = true;
856 popupMenu.AppendMenuIcon(eCmd_Diff, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS)), IDI_DIFF);
859 if(!selectedLeafs.empty())
861 if(AreAllFrom(selectedLeafs, L"refs/remotes/"))
863 if(bAddSeparator)
864 popupMenu.AppendMenu(MF_SEPARATOR);
865 CString menuItemName;
866 if(selectedLeafs.size() == 1)
867 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH);
868 else
869 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES, selectedLeafs.size());
871 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteBranch, menuItemName, IDI_DELETE);
872 bAddSeparator = true;
874 else if(AreAllFrom(selectedLeafs, L"refs/heads/"))
876 if(bAddSeparator)
877 popupMenu.AppendMenu(MF_SEPARATOR);
878 CString menuItemName;
879 if(selectedLeafs.size() == 1)
880 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH);
881 else
882 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES, selectedLeafs.size());
884 popupMenu.AppendMenuIcon(eCmd_DeleteBranch, menuItemName, IDI_DELETE);
885 bAddSeparator = true;
887 else if(AreAllFrom(selectedLeafs, L"refs/tags/"))
889 if(bAddSeparator)
890 popupMenu.AppendMenu(MF_SEPARATOR);
891 CString menuItemName;
892 if(selectedLeafs.size() == 1)
893 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETETAG);
894 else
895 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETETAGS, selectedLeafs.size());
897 popupMenu.AppendMenuIcon(eCmd_DeleteTag, menuItemName, IDI_DELETE);
898 bAddSeparator = true;
903 if(hTreePos!=NULL && selectedLeafs.empty())
905 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);
906 if(pTree->IsFrom(L"refs/remotes"))
908 if(bAddSeparator)
909 popupMenu.AppendMenu(MF_SEPARATOR);
910 popupMenu.AppendMenuIcon(eCmd_ManageRemotes, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES)), IDI_SETTINGS);
911 bAddSeparator = true;
912 if(selectedLeafs.empty())
914 int dummy = 0;//Needed for tokenize
915 remoteName = pTree->GetRefName();
916 remoteName = remoteName.Mid(13);
917 remoteName = remoteName.Tokenize(L"/", dummy);
918 if(!remoteName.IsEmpty())
920 CString temp;
921 temp.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
922 popupMenu.AppendMenuIcon(eCmd_Fetch, temp, IDI_PULL);
924 temp.LoadString(IDS_DELETEREMOTETAG);
925 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteTag, temp, IDI_DELETE);
929 if(pTree->IsFrom(L"refs/heads"))
931 if(bAddSeparator)
932 popupMenu.AppendMenu(MF_SEPARATOR);
933 CString temp;
934 temp.LoadString(IDS_MENUBRANCH);
935 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
937 if(pTree->IsFrom(L"refs/tags"))
939 if(bAddSeparator)
940 popupMenu.AppendMenu(MF_SEPARATOR);
941 CString temp;
942 temp.LoadString(IDS_MENUTAG);
943 popupMenu.AppendMenuIcon(eCmd_CreateTag, temp, IDI_TAG);
948 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
949 switch(cmd)
951 case eCmd_ViewLog:
953 CLogDlg dlg;
954 dlg.SetStartRef(selectedLeafs[0]->GetRefName());
955 dlg.DoModal();
957 break;
958 case eCmd_RepoBrowser:
959 CAppUtils::RunTortoiseProc(_T("/command:repobrowser /path:\"") + g_Git.m_CurrentDir + _T("\" /rev:") + selectedLeafs[0]->GetRefName());
960 break;
961 case eCmd_DeleteBranch:
962 case eCmd_DeleteRemoteBranch:
964 if(ConfirmDeleteRef(selectedLeafs))
965 DoDeleteRefs(selectedLeafs, true);
966 Refresh();
968 break;
969 case eCmd_DeleteTag:
971 if(ConfirmDeleteRef(selectedLeafs))
972 DoDeleteRefs(selectedLeafs, true);
973 Refresh();
975 break;
976 case eCmd_ShowReflog:
978 CRefLogDlg refLogDlg(this);
979 refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();
980 refLogDlg.DoModal();
982 break;
983 case eCmd_Fetch:
985 CAppUtils::Fetch(remoteName);
986 Refresh();
988 break;
989 case eCmd_DeleteRemoteTag:
991 CDeleteRemoteTagDlg deleteRemoteTagDlg;
992 deleteRemoteTagDlg.m_sRemote = remoteName;
993 deleteRemoteTagDlg.DoModal();
995 break;
996 case eCmd_Switch:
998 CAppUtils::Switch(selectedLeafs[0]->GetRefName());
1000 break;
1001 case eCmd_Rename:
1003 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1004 if(pos != NULL)
1005 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1007 break;
1008 case eCmd_AddRemote:
1010 CAddRemoteDlg(this).DoModal();
1011 Refresh();
1013 break;
1014 case eCmd_ManageRemotes:
1016 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS)), new CSettingGitRemote(g_Git.m_CurrentDir), this).DoModal();
1017 // CSettingGitRemote W_Remotes(m_cmdPath);
1018 // W_Remotes.DoModal();
1019 Refresh();
1021 break;
1022 case eCmd_CreateBranch:
1024 CString *commitHash = NULL;
1025 if (selectedLeafs.size() == 1)
1026 commitHash = &(selectedLeafs[0]->m_csRefHash);
1027 CAppUtils::CreateBranchTag(false, commitHash);
1028 Refresh();
1030 break;
1031 case eCmd_CreateTag:
1033 CAppUtils::CreateBranchTag(true);
1034 Refresh();
1036 break;
1037 case eCmd_Diff:
1039 CFileDiffDlg dlg;
1040 dlg.SetDiff(
1041 NULL,
1042 selectedLeafs[0]->m_csRefHash,
1043 selectedLeafs[1]->m_csRefHash);
1044 dlg.DoModal();
1046 break;
1047 case eCmd_EditBranchDescription:
1049 CInputDlg dlg;
1050 dlg.m_sHintText = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1051 dlg.m_sInputText = selectedLeafs[0]->m_csDescription;
1052 dlg.m_sTitle = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1053 dlg.m_bUseLogWidth = true;
1054 if(dlg.DoModal() == IDOK)
1056 CString key;
1057 key.Format(_T("branch.%s.description"), selectedLeafs[0]->m_csRefName);
1058 dlg.m_sInputText.Replace(_T("\r"), _T(""));
1059 dlg.m_sInputText.Trim();
1060 if (dlg.m_sInputText.IsEmpty())
1061 g_Git.UnsetConfigValue(key);
1062 else
1063 g_Git.SetConfigValue(key, dlg.m_sInputText);
1064 Refresh();
1067 break;
1071 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree& leafs, const wchar_t* from)
1073 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
1074 if(!(*i)->IsFrom(from))
1075 return false;
1076 return true;
1079 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
1081 if (pMsg->message == WM_KEYDOWN)
1083 switch (pMsg->wParam)
1085 /* case VK_RETURN:
1087 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1089 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1091 PostMessage(WM_COMMAND, IDOK);
1093 return TRUE;
1096 break;
1097 */ case VK_F2:
1099 if(pMsg->hwnd == m_ListRefLeafs.m_hWnd)
1101 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1102 if(pos != NULL)
1103 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1106 break;
1108 case VK_F5:
1110 Refresh();
1112 break;
1117 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
1120 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1122 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1123 *pResult = 0;
1125 if(m_currSortCol == pNMLV->iSubItem)
1126 m_currSortDesc = !m_currSortDesc;
1127 else
1129 m_currSortCol = pNMLV->iSubItem;
1130 m_currSortDesc = false;
1133 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
1134 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
1136 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
1139 void CBrowseRefsDlg::OnDestroy()
1141 m_pickedRef = GetSelectedRef(true, false);
1143 CResizableStandAloneDialog::OnDestroy();
1146 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1148 UNREFERENCED_PARAMETER(pNMHDR);
1149 *pResult = 0;
1151 EndDialog(IDOK);
1154 CString CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef, int pickRef_Kind)
1156 CBrowseRefsDlg dlg(CString(),NULL);
1158 if(initialRef.IsEmpty())
1159 initialRef = L"HEAD";
1160 dlg.m_initialRef = initialRef;
1161 dlg.m_pickRef_Kind = pickRef_Kind;
1162 dlg.m_bPickOne = true;
1164 if(dlg.DoModal() != IDOK)
1165 return CString();
1167 return dlg.m_pickedRef;
1170 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)
1172 CString origRef;
1173 pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);
1174 CString resultRef = PickRef(false,origRef,pickRef_Kind);
1175 if(resultRef.IsEmpty())
1176 return false;
1177 if(wcsncmp(resultRef,L"refs/",5)==0)
1178 resultRef = resultRef.Mid(5);
1179 // if(wcsncmp(resultRef,L"heads/",6)==0)
1180 // resultRef = resultRef.Mid(6);
1182 //Find closest match of choice in combobox
1183 int ixFound = -1;
1184 int matchLength = 0;
1185 CString comboRefName;
1186 for(int i = 0; i < pComboBox->GetCount(); ++i)
1188 pComboBox->GetLBText(i, comboRefName);
1189 if(comboRefName.Find(L'/') < 0 && !comboRefName.IsEmpty())
1190 comboRefName.Insert(0,L"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1191 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)
1193 matchLength = comboRefName.GetLength();
1194 ixFound = i;
1197 if(ixFound >= 0)
1198 pComboBox->SetCurSel(ixFound);
1199 else
1200 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)
1202 return true;
1205 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1207 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1208 *pResult = FALSE;
1210 if(pDispInfo->item.pszText == NULL)
1211 return; //User canceled changing
1213 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1215 if(!pTree->IsFrom(L"refs/heads"))
1217 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES, IDS_APPNAME, MB_OK | MB_ICONERROR);
1218 return;
1221 CString origName = pTree->GetRefName().Mid(11);
1223 CString newName;
1224 if(m_pListCtrlRoot != NULL)
1225 newName = m_pListCtrlRoot->GetRefName() + L'/';
1226 newName += pDispInfo->item.pszText;
1228 if(wcsncmp(newName,L"refs/heads/",11)!=0)
1230 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE, IDS_APPNAME, MB_OK | MB_ICONERROR);
1231 return;
1234 CString newNameTrunced = newName.Mid(11);
1236 CString errorMsg;
1237 if(g_Git.Run(L"git branch -m \"" + origName + L"\" \"" + newNameTrunced + L"\"", &errorMsg, CP_UTF8) != 0)
1239 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1240 return;
1242 //Do as if it failed to rename. Let Refresh() do the job.
1243 //*pResult = TRUE;
1245 Refresh(newName);
1247 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1249 // AfxMessageBox(W_csPopup);
1253 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1255 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1256 *pResult = FALSE;
1258 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1260 if(!pTree->IsFrom(L"refs/heads"))
1262 *pResult = TRUE; //Dont allow renaming any other things then branches at the moment.
1263 return;
1267 void CBrowseRefsDlg::OnEnChangeEditFilter()
1269 SetTimer(IDT_FILTER, 1000, NULL);
1272 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent)
1274 if (nIDEvent == IDT_FILTER)
1276 KillTimer(IDT_FILTER);
1277 FillListCtrlForTreeNode(m_RefTreeCtrl.GetSelectedItem());
1280 CResizableStandAloneDialog::OnTimer(nIDEvent);
1283 LRESULT CBrowseRefsDlg::OnClickedInfoIcon(WPARAM /*wParam*/, LPARAM lParam)
1285 // FIXME: x64 version would get this function called with unexpected parameters.
1286 if (!lParam)
1287 return 0;
1289 RECT * rect = (LPRECT)lParam;
1290 CPoint point;
1291 CString temp;
1292 point = CPoint(rect->left, rect->bottom);
1293 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1294 CMenu popup;
1295 if (popup.CreatePopupMenu())
1297 temp.LoadString(IDS_LOG_FILTER_REFNAME);
1298 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME), LOGFILTER_REFNAME, temp);
1300 temp.LoadString(IDS_LOG_FILTER_SUBJECT);
1301 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT), LOGFILTER_SUBJECT, temp);
1303 temp.LoadString(IDS_LOG_FILTER_AUTHORS);
1304 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS), LOGFILTER_AUTHORS, temp);
1306 temp.LoadString(IDS_LOG_FILTER_REVS);
1307 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS), LOGFILTER_REVS, temp);
1309 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1310 if (selection != 0)
1312 m_SelectedFilters ^= selection;
1313 SetFilterCueText();
1314 SetTimer(IDT_FILTER, 1000, NULL);
1317 return 0L;
1320 void CBrowseRefsDlg::SetFilterCueText()
1322 CString temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY));
1323 temp += _T(" ");
1325 if (m_SelectedFilters & LOGFILTER_REFNAME)
1326 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME));
1328 if (m_SelectedFilters & LOGFILTER_SUBJECT)
1330 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1331 temp += _T(", ");
1332 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT));
1335 if (m_SelectedFilters & LOGFILTER_AUTHORS)
1337 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1338 temp += _T(", ");
1339 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS));
1342 if (m_SelectedFilters & LOGFILTER_REVS)
1344 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1345 temp += _T(", ");
1346 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS));
1349 // to make the cue banner text appear more to the right of the edit control
1350 temp = _T(" ") + temp;
1351 m_ctrlFilter.SetCueBanner(temp.TrimRight());