Moved #include "git2.h" to stdafx.h
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blob2359fbcb345c88152a4c0a6e9225a672564061e6
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2013 - 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"
38 void SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)
40 if (control == NULL)
41 return;
42 // set the sort arrow
43 CHeaderCtrl * pHeader = control->GetHeaderCtrl();
44 HDITEM HeaderItem = {0};
45 HeaderItem.mask = HDI_FORMAT;
46 for (int i=0; i<pHeader->GetItemCount(); ++i)
48 pHeader->GetItem(i, &HeaderItem);
49 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
50 pHeader->SetItem(i, &HeaderItem);
52 if (nColumn >= 0)
54 pHeader->GetItem(nColumn, &HeaderItem);
55 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);
56 pHeader->SetItem(nColumn, &HeaderItem);
60 class CRefLeafListCompareFunc
62 public:
63 CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){
64 m_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER);
65 if (m_bSortLogical)
66 m_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE);
69 static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
71 return ((CRefLeafListCompareFunc*)lParamSort)->Compare(lParam1,lParam2);
74 int Compare(LPARAM lParam1, LPARAM lParam2)
76 return Compare(
77 (CShadowTree*)m_pList->GetItemData((int)lParam1),
78 (CShadowTree*)m_pList->GetItemData((int)lParam2));
81 int Compare(CShadowTree* pLeft, CShadowTree* pRight)
83 int result=CompareNoDesc(pLeft,pRight);
84 if(m_desc)
85 return -result;
86 return result;
89 int CompareNoDesc(CShadowTree* pLeft, CShadowTree* pRight)
91 switch(m_col)
93 case CBrowseRefsDlg::eCol_Name: return SortStrCmp(pLeft->GetRefName(), pRight->GetRefName());
94 case CBrowseRefsDlg::eCol_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);
95 case CBrowseRefsDlg::eCol_Msg: return SortStrCmp(pLeft->m_csSubject, pRight->m_csSubject);
96 case CBrowseRefsDlg::eCol_LastAuthor: return SortStrCmp(pLeft->m_csAuthor, pRight->m_csAuthor);
97 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);
98 case CBrowseRefsDlg::eCol_Description: return SortStrCmp(pLeft->m_csDescription, pRight->m_csDescription);
100 return 0;
102 int SortStrCmp(CString left, CString right)
104 if (m_bSortLogical)
105 return StrCmpLogicalW(left, right);
106 return StrCmpI(left, right);
109 int m_col;
110 bool m_desc;
111 CListCtrl* m_pList;
112 bool m_bSortLogical;
115 // CBrowseRefsDlg dialog
117 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)
119 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)
120 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),
121 m_cmdPath(cmdPath),
122 m_currSortCol(0),
123 m_currSortDesc(false),
124 m_initialRef(L"HEAD"),
125 m_pickRef_Kind(gPickRef_All),
126 m_pListCtrlRoot(NULL),
127 m_bHasWC(true),
128 m_SelectedFilters(LOGFILTER_ALL),
129 m_bPickOne(false)
134 CBrowseRefsDlg::~CBrowseRefsDlg()
138 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)
140 CDialog::DoDataExchange(pDX);
141 DDX_Control(pDX, IDC_TREE_REF, m_RefTreeCtrl);
142 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_ListRefLeafs);
143 DDX_Control(pDX, IDC_BROWSEREFS_EDIT_FILTER, m_ctrlFilter);
147 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)
148 ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)
149 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)
150 ON_WM_CONTEXTMENU()
151 ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)
152 ON_WM_DESTROY()
153 ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)
154 ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs)
155 ON_NOTIFY(LVN_BEGINLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs)
156 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER, &CBrowseRefsDlg::OnEnChangeEditFilter)
157 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED, OnClickedInfoIcon)
158 ON_WM_TIMER()
159 END_MESSAGE_MAP()
162 // CBrowseRefsDlg message handlers
164 void CBrowseRefsDlg::OnBnClickedOk()
166 OnOK();
169 BOOL CBrowseRefsDlg::OnInitDialog()
171 CResizableStandAloneDialog::OnInitDialog();
172 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
174 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
175 m_ctrlFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
176 m_ctrlFilter.SetInfoIcon(IDI_FILTEREDIT);
177 SetFilterCueText();
179 AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);
180 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
181 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER, TOP_LEFT);
182 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER, TOP_LEFT, TOP_RIGHT);
183 AddAnchor(IDHELP, BOTTOM_RIGHT);
185 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
186 CString temp;
187 temp.LoadString(IDS_BRANCHNAME);
188 m_ListRefLeafs.InsertColumn(eCol_Name, temp, 0, 150);
189 temp.LoadString(IDS_DATELASTCOMMIT);
190 m_ListRefLeafs.InsertColumn(eCol_Date, temp, 0, 100);
191 temp.LoadString(IDS_LASTCOMMIT);
192 m_ListRefLeafs.InsertColumn(eCol_Msg, temp, 0, 300);
193 temp.LoadString(IDS_LASTAUTHOR);
194 m_ListRefLeafs.InsertColumn(eCol_LastAuthor, temp, 0, 100);
195 temp.LoadString(IDS_HASH);
196 m_ListRefLeafs.InsertColumn(eCol_Hash, temp, 0, 80);
197 temp.LoadString(IDS_DESCRIPTION);
198 m_ListRefLeafs.InsertColumn(eCol_Description, temp, 0, 80);
200 AddAnchor(IDOK,BOTTOM_RIGHT);
201 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
203 Refresh(m_initialRef);
205 EnableSaveRestore(L"BrowseRefs");
207 CString sWindowTitle;
208 GetWindowText(sWindowTitle);
209 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
211 m_bHasWC = !g_GitAdminDir.IsBareRepo(g_Git.m_CurrentDir);
213 if (m_bPickOne)
214 m_ListRefLeafs.ModifyStyle(0, LVS_SINGLESEL);
216 m_ListRefLeafs.SetFocus();
217 return FALSE;
220 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
222 int posSlash=nameLeft.Find('/');
223 CString nameSub;
224 if(posSlash<0)
226 nameSub=nameLeft;
227 nameLeft.Empty();//Nothing left
229 else
231 nameSub=nameLeft.Left(posSlash);
232 nameLeft=nameLeft.Mid(posSlash+1);
234 if(nameSub.IsEmpty())
235 return NULL;
237 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
238 return NULL;
240 CShadowTree& nextNode=m_ShadowTree[nameSub];
241 nextNode.m_csRefName=nameSub;
242 nextNode.m_pParent=this;
243 return &nextNode;
246 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)
248 if(IsLeaf())
250 if(m_csRefName.GetLength() > partialRefName.GetLength())
251 return NULL;
252 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)
254 //Match of leaf name. Try match on total name.
255 CString totalRefName = GetRefName();
256 if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)
257 return this; //Also match. Found.
260 else
262 //Not a leaf. Search all nodes.
263 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)
265 CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);
266 if(pSubtree != NULL)
267 return pSubtree; //Found
270 return NULL;//Not found
274 typedef std::map<CString,CString> MAP_STRING_STRING;
276 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf, bool pickFirstSelIfMultiSel)
278 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
279 //List ctrl selection?
280 if(pos && (pickFirstSelIfMultiSel || m_ListRefLeafs.GetSelectedCount() == 1))
282 //A leaf is selected
283 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(
284 m_ListRefLeafs.GetNextSelectedItem(pos));
285 return pTree->GetRefName();
287 else if(!onlyIfLeaf)
289 //Tree ctrl selection?
290 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
291 if(hTree!=NULL)
293 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);
294 return pTree->GetRefName();
297 return CString();//None
300 static int GetBranchDescriptionsCallback(const git_config_entry *entry, void *data)
302 MAP_STRING_STRING *descriptions = (MAP_STRING_STRING *) data;
303 CString key = CUnicodeUtils::GetUnicode(entry->name, CP_UTF8);
304 CString val = CUnicodeUtils::GetUnicode(entry->value, CP_UTF8);
305 descriptions->insert(make_pair(key.Mid(7, key.GetLength() - 7 - 12), val)); // 7: branch., 12: .description
306 return 0;
309 MAP_STRING_STRING GetBranchDescriptions()
311 MAP_STRING_STRING descriptions;
312 git_config * config;
313 git_config_new(&config);
314 CStringA projectConfigA = CUnicodeUtils::GetMulti(g_Git.GetGitLocalConfig(), CP_UTF8);
315 git_config_add_file_ondisk(config, projectConfigA.GetBuffer(), 3, FALSE);
316 projectConfigA.ReleaseBuffer();
317 git_config_foreach_match(config, "branch\\..*\\.description", GetBranchDescriptionsCallback, &descriptions);
318 git_config_free(config);
319 return descriptions;
322 void CBrowseRefsDlg::Refresh(CString selectRef)
324 // m_RefMap.clear();
325 // g_Git.GetMapHashToFriendName(m_RefMap);
327 if(!selectRef.IsEmpty())
329 if(selectRef == "HEAD")
331 selectRef = g_Git.GetSymbolicRef(selectRef, false);
334 else
336 selectRef = GetSelectedRef(false, true);
339 m_RefTreeCtrl.DeleteAllItems();
340 m_ListRefLeafs.DeleteAllItems();
341 m_TreeRoot.m_ShadowTree.clear();
342 m_TreeRoot.m_csRefName = "refs";
343 m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);
344 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);
346 CString allRefs, error;
347 if (g_Git.Run(L"git for-each-ref --format="
348 L"%(refname)%04"
349 L"%(objectname)%04"
350 L"%(authordate:relative)%04"
351 L"%(subject)%04"
352 L"%(authorname)%04"
353 L"%(authordate:iso8601)%03",
354 &allRefs, &error, CP_UTF8))
356 CMessageBox::Show(NULL, CString(_T("Get refs failed\n")) + error, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
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);
944 temp.LoadString(IDS_PROC_BROWSEREFS_DELETEALLTAGS);
945 popupMenu.AppendMenuIcon(eCmd_DeleteAllTags, temp, IDI_DELETE);
950 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
951 switch(cmd)
953 case eCmd_ViewLog:
955 CLogDlg dlg;
956 dlg.SetStartRef(selectedLeafs[0]->GetRefName());
957 dlg.DoModal();
959 break;
960 case eCmd_RepoBrowser:
961 CAppUtils::RunTortoiseGitProc(_T("/command:repobrowser /path:\"") + g_Git.m_CurrentDir + _T("\" /rev:") + selectedLeafs[0]->GetRefName());
962 break;
963 case eCmd_DeleteBranch:
964 case eCmd_DeleteRemoteBranch:
966 if(ConfirmDeleteRef(selectedLeafs))
967 DoDeleteRefs(selectedLeafs, true);
968 Refresh();
970 break;
971 case eCmd_DeleteTag:
973 if(ConfirmDeleteRef(selectedLeafs))
974 DoDeleteRefs(selectedLeafs, true);
975 Refresh();
977 break;
978 case eCmd_ShowReflog:
980 CRefLogDlg refLogDlg(this);
981 refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();
982 refLogDlg.DoModal();
984 break;
985 case eCmd_Fetch:
987 CAppUtils::Fetch(remoteName);
988 Refresh();
990 break;
991 case eCmd_DeleteRemoteTag:
993 CDeleteRemoteTagDlg deleteRemoteTagDlg;
994 deleteRemoteTagDlg.m_sRemote = remoteName;
995 deleteRemoteTagDlg.DoModal();
997 break;
998 case eCmd_Switch:
1000 CAppUtils::Switch(selectedLeafs[0]->GetRefName());
1002 break;
1003 case eCmd_Rename:
1005 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1006 if(pos != NULL)
1007 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1009 break;
1010 case eCmd_AddRemote:
1012 CAddRemoteDlg(this).DoModal();
1013 Refresh();
1015 break;
1016 case eCmd_ManageRemotes:
1018 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS)), new CSettingGitRemote(g_Git.m_CurrentDir), this).DoModal();
1019 // CSettingGitRemote W_Remotes(m_cmdPath);
1020 // W_Remotes.DoModal();
1021 Refresh();
1023 break;
1024 case eCmd_CreateBranch:
1026 CString *commitHash = NULL;
1027 if (selectedLeafs.size() == 1)
1028 commitHash = &(selectedLeafs[0]->m_csRefHash);
1029 CAppUtils::CreateBranchTag(false, commitHash);
1030 Refresh();
1032 break;
1033 case eCmd_CreateTag:
1035 CAppUtils::CreateBranchTag(true);
1036 Refresh();
1038 break;
1039 case eCmd_DeleteAllTags:
1041 for (int i = 0; i < m_ListRefLeafs.GetItemCount(); ++i)
1043 m_ListRefLeafs.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
1044 selectedLeafs.push_back((CShadowTree*)m_ListRefLeafs.GetItemData(i));
1046 if (ConfirmDeleteRef(selectedLeafs))
1047 DoDeleteRefs(selectedLeafs, true);
1048 Refresh();
1050 break;
1051 case eCmd_Diff:
1053 CFileDiffDlg dlg;
1054 dlg.SetDiff(
1055 NULL,
1056 selectedLeafs[0]->m_csRefHash,
1057 selectedLeafs[1]->m_csRefHash);
1058 dlg.DoModal();
1060 break;
1061 case eCmd_EditBranchDescription:
1063 CInputDlg dlg;
1064 dlg.m_sHintText = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1065 dlg.m_sInputText = selectedLeafs[0]->m_csDescription;
1066 dlg.m_sTitle = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1067 dlg.m_bUseLogWidth = true;
1068 if(dlg.DoModal() == IDOK)
1070 CString key;
1071 key.Format(_T("branch.%s.description"), selectedLeafs[0]->m_csRefName);
1072 dlg.m_sInputText.Replace(_T("\r"), _T(""));
1073 dlg.m_sInputText.Trim();
1074 if (dlg.m_sInputText.IsEmpty())
1075 g_Git.UnsetConfigValue(key);
1076 else
1077 g_Git.SetConfigValue(key, dlg.m_sInputText);
1078 Refresh();
1081 break;
1085 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree& leafs, const wchar_t* from)
1087 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
1088 if(!(*i)->IsFrom(from))
1089 return false;
1090 return true;
1093 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
1095 if (pMsg->message == WM_KEYDOWN)
1097 switch (pMsg->wParam)
1099 /* case VK_RETURN:
1101 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1103 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1105 PostMessage(WM_COMMAND, IDOK);
1107 return TRUE;
1110 break;
1111 */ case VK_F2:
1113 if(pMsg->hwnd == m_ListRefLeafs.m_hWnd)
1115 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1116 if(pos != NULL)
1117 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1120 break;
1122 case VK_F5:
1124 Refresh();
1126 break;
1131 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
1134 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1136 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1137 *pResult = 0;
1139 if(m_currSortCol == pNMLV->iSubItem)
1140 m_currSortDesc = !m_currSortDesc;
1141 else
1143 m_currSortCol = pNMLV->iSubItem;
1144 m_currSortDesc = false;
1147 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
1148 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
1150 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
1153 void CBrowseRefsDlg::OnDestroy()
1155 m_pickedRef = GetSelectedRef(true, false);
1157 CResizableStandAloneDialog::OnDestroy();
1160 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR * /*pNMHDR*/, LRESULT *pResult)
1162 *pResult = 0;
1164 EndDialog(IDOK);
1167 CString CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef, int pickRef_Kind)
1169 CBrowseRefsDlg dlg(CString(),NULL);
1171 if(initialRef.IsEmpty())
1172 initialRef = L"HEAD";
1173 dlg.m_initialRef = initialRef;
1174 dlg.m_pickRef_Kind = pickRef_Kind;
1175 dlg.m_bPickOne = true;
1177 if(dlg.DoModal() != IDOK)
1178 return CString();
1180 return dlg.m_pickedRef;
1183 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)
1185 CString origRef;
1186 pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);
1187 CString resultRef = PickRef(false,origRef,pickRef_Kind);
1188 if(resultRef.IsEmpty())
1189 return false;
1190 if(wcsncmp(resultRef,L"refs/",5)==0)
1191 resultRef = resultRef.Mid(5);
1192 // if(wcsncmp(resultRef,L"heads/",6)==0)
1193 // resultRef = resultRef.Mid(6);
1195 //Find closest match of choice in combobox
1196 int ixFound = -1;
1197 int matchLength = 0;
1198 CString comboRefName;
1199 for(int i = 0; i < pComboBox->GetCount(); ++i)
1201 pComboBox->GetLBText(i, comboRefName);
1202 if(comboRefName.Find(L'/') < 0 && !comboRefName.IsEmpty())
1203 comboRefName.Insert(0,L"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1204 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)
1206 matchLength = comboRefName.GetLength();
1207 ixFound = i;
1210 if(ixFound >= 0)
1211 pComboBox->SetCurSel(ixFound);
1212 else
1213 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)
1215 return true;
1218 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1220 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1221 *pResult = FALSE;
1223 if(pDispInfo->item.pszText == NULL)
1224 return; //User canceled changing
1226 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1228 if(!pTree->IsFrom(L"refs/heads"))
1230 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES, IDS_APPNAME, MB_OK | MB_ICONERROR);
1231 return;
1234 CString origName = pTree->GetRefName().Mid(11);
1236 CString newName;
1237 if(m_pListCtrlRoot != NULL)
1238 newName = m_pListCtrlRoot->GetRefName() + L'/';
1239 newName += pDispInfo->item.pszText;
1241 if(wcsncmp(newName,L"refs/heads/",11)!=0)
1243 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE, IDS_APPNAME, MB_OK | MB_ICONERROR);
1244 return;
1247 CString newNameTrunced = newName.Mid(11);
1249 CString errorMsg;
1250 if(g_Git.Run(L"git branch -m \"" + origName + L"\" \"" + newNameTrunced + L"\"", &errorMsg, CP_UTF8) != 0)
1252 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1253 return;
1255 //Do as if it failed to rename. Let Refresh() do the job.
1256 //*pResult = TRUE;
1258 Refresh(newName);
1260 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1262 // AfxMessageBox(W_csPopup);
1266 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1268 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1269 *pResult = FALSE;
1271 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1273 if(!pTree->IsFrom(L"refs/heads"))
1275 *pResult = TRUE; //Dont allow renaming any other things then branches at the moment.
1276 return;
1280 void CBrowseRefsDlg::OnEnChangeEditFilter()
1282 SetTimer(IDT_FILTER, 1000, NULL);
1285 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent)
1287 if (nIDEvent == IDT_FILTER)
1289 KillTimer(IDT_FILTER);
1290 FillListCtrlForTreeNode(m_RefTreeCtrl.GetSelectedItem());
1293 CResizableStandAloneDialog::OnTimer(nIDEvent);
1296 LRESULT CBrowseRefsDlg::OnClickedInfoIcon(WPARAM /*wParam*/, LPARAM lParam)
1298 // FIXME: x64 version would get this function called with unexpected parameters.
1299 if (!lParam)
1300 return 0;
1302 RECT * rect = (LPRECT)lParam;
1303 CPoint point;
1304 CString temp;
1305 point = CPoint(rect->left, rect->bottom);
1306 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1307 CMenu popup;
1308 if (popup.CreatePopupMenu())
1310 temp.LoadString(IDS_LOG_FILTER_REFNAME);
1311 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME), LOGFILTER_REFNAME, temp);
1313 temp.LoadString(IDS_LOG_FILTER_SUBJECT);
1314 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT), LOGFILTER_SUBJECT, temp);
1316 temp.LoadString(IDS_LOG_FILTER_AUTHORS);
1317 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS), LOGFILTER_AUTHORS, temp);
1319 temp.LoadString(IDS_LOG_FILTER_REVS);
1320 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS), LOGFILTER_REVS, temp);
1322 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1323 if (selection != 0)
1325 m_SelectedFilters ^= selection;
1326 SetFilterCueText();
1327 SetTimer(IDT_FILTER, 1000, NULL);
1330 return 0L;
1333 void CBrowseRefsDlg::SetFilterCueText()
1335 CString temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY));
1336 temp += _T(" ");
1338 if (m_SelectedFilters & LOGFILTER_REFNAME)
1339 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME));
1341 if (m_SelectedFilters & LOGFILTER_SUBJECT)
1343 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1344 temp += _T(", ");
1345 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT));
1348 if (m_SelectedFilters & LOGFILTER_AUTHORS)
1350 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1351 temp += _T(", ");
1352 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS));
1355 if (m_SelectedFilters & LOGFILTER_REVS)
1357 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1358 temp += _T(", ");
1359 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS));
1362 // to make the cue banner text appear more to the right of the edit control
1363 temp = _T(" ") + temp;
1364 m_ctrlFilter.SetCueBanner(temp.TrimRight());