432e0a1b0d577d225c6c8521d7a21fbe2dba0d5f
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blob432e0a1b0d577d225c6c8521d7a21fbe2dba0d5f
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 "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 git_config_entry *entry, void *data)
303 MAP_STRING_STRING *descriptions = (MAP_STRING_STRING *) data;
304 CString key = CUnicodeUtils::GetUnicode(entry->name, CP_UTF8);
305 CString val = CUnicodeUtils::GetUnicode(entry->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.GetGitLocalConfig(), CP_UTF8);
316 git_config_add_file_ondisk(config, projectConfigA.GetBuffer(), 3, FALSE);
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, error;
348 if (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, &error, CP_UTF8))
357 CMessageBox::Show(NULL, CString(_T("Get refs failed\n")) + error, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
360 int linePos=0;
361 CString singleRef;
363 MAP_STRING_STRING refMap;
365 //First sort on ref name
366 while(!(singleRef=allRefs.Tokenize(L"\03",linePos)).IsEmpty())
368 singleRef.TrimLeft(L"\r\n");
369 int valuePos=0;
370 CString refName=singleRef.Tokenize(L"\04",valuePos);
371 if(refName.IsEmpty())
372 continue;
373 CString refRest=singleRef.Mid(valuePos);
376 //Use ref based on m_pickRef_Kind
377 if(wcsncmp(refName,L"refs/heads",10)==0 && !(m_pickRef_Kind & gPickRef_Head) )
378 continue; //Skip
379 if(wcsncmp(refName,L"refs/tags",9)==0 && !(m_pickRef_Kind & gPickRef_Tag) )
380 continue; //Skip
381 if(wcsncmp(refName,L"refs/remotes",12)==0 && !(m_pickRef_Kind & gPickRef_Remote) )
382 continue; //Skip
384 refMap[refName] = refRest; //Use
387 MAP_STRING_STRING descriptions = GetBranchDescriptions();
389 //Populate ref tree
390 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
392 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
393 CString values=iterRefMap->second;
394 values.Replace(L"\04" L"\04",L"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
396 int valuePos=0;
397 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
398 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
399 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
400 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
401 treeLeaf.m_csDate_Iso8601= values.Tokenize(L"\04",valuePos);
403 if (wcsncmp(iterRefMap->first, L"refs/heads", 10) == 0)
404 treeLeaf.m_csDescription = descriptions[treeLeaf.m_csRefName];
408 if(selectRef.IsEmpty() || !SelectRef(selectRef, false))
409 //Probably not on a branch. Select root node.
410 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);
414 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
416 if(!bExactMatch)
418 CString newRefName = GetFullRefName(refName);
419 if(!newRefName.IsEmpty())
420 refName = newRefName;
421 //else refName is not a valid ref. Try to select as good as possible.
423 if(_wcsnicmp(refName, L"refs/", 5) != 0)
424 return false; // Not a ref name
426 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
427 if(treeLeafHead.m_hTree != NULL)
429 //Not a leaf. Select tree node and return
430 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
431 return true;
434 if(treeLeafHead.m_pParent==NULL)
435 return false; //Weird... should not occur.
437 //This is the current head.
438 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
440 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
442 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
443 if(pCurrShadowTree == &treeLeafHead)
445 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
446 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
450 return true;
453 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
455 if(pTreePos==NULL)
457 if(_wcsnicmp(refName, L"refs/", 5) == 0)
458 refName=refName.Mid(5);
459 pTreePos=&m_TreeRoot;
461 if(refName.IsEmpty())
462 return *pTreePos;//Found leaf
464 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
465 if(pNextTree==NULL)
467 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
468 ASSERT(!bCreateIfNotExist);
469 return *pTreePos;
472 if(!refName.IsEmpty())
474 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
475 //Leafs are for the list control.
476 if(pNextTree->m_hTree==NULL)
478 //New tree. Create node in control.
479 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
480 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
484 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
488 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
490 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
491 *pResult = 0;
493 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
496 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
498 m_ListRefLeafs.DeleteAllItems();
500 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
501 if(pTree==NULL)
503 ASSERT(FALSE);
504 return;
506 FillListCtrlForShadowTree(pTree,L"",true);
509 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
511 if(pTree->IsLeaf())
513 CString filter;
514 m_ctrlFilter.GetWindowText(filter);
515 filter.MakeLower();
516 CString ref = refNamePrefix + pTree->m_csRefName;
517 if (!(pTree->m_csRefName.IsEmpty() || pTree->m_csRefName == "refs" && pTree->m_pParent == NULL) && IsMatchFilter(pTree, ref, filter))
519 int indexItem = m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(), L"");
521 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
522 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, ref);
523 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);
524 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
525 m_ListRefLeafs.SetItemText(indexItem,eCol_LastAuthor, pTree->m_csAuthor);
526 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
527 int pos = 0;
528 m_ListRefLeafs.SetItemText(indexItem,eCol_Description, pTree->m_csDescription.Tokenize(_T("\n"), pos));
531 else
534 CString csThisName;
535 if(!isFirstLevel)
536 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
537 else
538 m_pListCtrlRoot = pTree;
539 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
541 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
544 if (isFirstLevel)
546 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
547 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
549 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
553 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree* pTree, const CString &ref, const CString &filter)
555 if (m_SelectedFilters & LOGFILTER_REFNAME)
557 CString msg = ref;
558 msg = msg.MakeLower();
560 if (msg.Find(filter) >= 0)
561 return true;
564 if (m_SelectedFilters & LOGFILTER_SUBJECT)
566 CString msg = pTree->m_csSubject;
567 msg = msg.MakeLower();
569 if (msg.Find(filter) >= 0)
570 return true;
573 if (m_SelectedFilters & LOGFILTER_AUTHORS)
575 CString msg = pTree->m_csAuthor;
576 msg = msg.MakeLower();
578 if (msg.Find(filter) >= 0)
579 return true;
582 if (m_SelectedFilters & LOGFILTER_REVS)
584 CString msg = pTree->m_csRefHash;
585 msg = msg.MakeLower();
587 if (msg.Find(filter) >= 0)
588 return true;
590 return false;
593 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree& leafs)
595 ASSERT(!leafs.empty());
597 CString csMessage;
598 UINT mbIcon=MB_ICONQUESTION;
600 bool bIsRemoteBranch = false;
601 bool bIsBranch = false;
602 if (leafs[0]->IsFrom(L"refs/remotes")) {bIsBranch = true; bIsRemoteBranch = true;}
603 else if (leafs[0]->IsFrom(L"refs/heads")) {bIsBranch = true;}
605 if(bIsBranch)
607 if(leafs.size() == 1)
609 CString branchToDelete = leafs[0]->GetRefName().Mid(bIsRemoteBranch ? 13 : 11);
610 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, branchToDelete);
612 //Check if branch is fully merged in HEAD
613 if (!g_Git.IsFastForward(leafs[0]->GetRefName(), _T("HEAD")))
615 csMessage += L"\r\n\r\n";
616 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED));
617 mbIcon = MB_ICONWARNING;
620 if(bIsRemoteBranch)
622 csMessage += L"\r\n\r\n";
623 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
624 mbIcon = MB_ICONWARNING;
627 else
629 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
631 csMessage += L"\r\n\r\n";
632 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK));
633 mbIcon = MB_ICONWARNING;
635 if(bIsRemoteBranch)
637 csMessage += L"\r\n\r\n";
638 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
639 mbIcon = MB_ICONWARNING;
644 else if(leafs[0]->IsFrom(L"refs/tags"))
646 if(leafs.size() == 1)
648 CString tagToDelete = leafs[0]->GetRefName().Mid(10);
649 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, tagToDelete);
651 else
653 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
657 return CMessageBox::Show(m_hWnd, csMessage, _T("TortoiseGit"), MB_YESNO | mbIcon) == IDYES;
661 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree& leafs, bool bForce)
663 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
664 if(!DoDeleteRef((*i)->GetRefName(), bForce))
665 return false;
666 return true;
669 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)
671 bool bIsRemoteBranch = false;
672 bool bIsBranch = false;
673 if (wcsncmp(completeRefName, L"refs/remotes",12)==0) {bIsBranch = true; bIsRemoteBranch = true;}
674 else if (wcsncmp(completeRefName, L"refs/heads",10)==0) {bIsBranch = true;}
676 if(bIsBranch)
678 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);
679 CString cmd;
680 if(bIsRemoteBranch)
682 int slash = branchToDelete.Find(L'/');
683 if(slash < 0)
684 return false;
685 CString remoteName = branchToDelete.Left(slash);
686 CString remoteBranchToDelete = branchToDelete.Mid(slash + 1);
688 if(CAppUtils::IsSSHPutty())
690 CAppUtils::LaunchPAgent(NULL, &remoteName);
693 cmd.Format(L"git.exe push \"%s\" :refs/heads/%s", remoteName, remoteBranchToDelete);
695 else
696 cmd.Format(L"git.exe branch -%c -- %s",bForce?L'D':L'd',branchToDelete);
697 CString errorMsg;
698 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
700 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
701 return false;
704 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
706 CString tagToDelete = completeRefName.Mid(10);
707 CString cmd;
708 cmd.Format(L"git.exe tag -d -- %s",tagToDelete);
709 CString errorMsg;
710 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
712 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
713 return false;
716 return true;
719 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
721 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
722 if(pLeaf == NULL)
723 return CString();
724 return pLeaf->GetRefName();
728 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
730 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
731 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
734 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
736 CPoint clientPoint=point;
737 m_RefTreeCtrl.ScreenToClient(&clientPoint);
739 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
740 if(hTreeItem!=NULL)
741 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
743 VectorPShadowTree tree;
744 ShowContextMenu(point,hTreeItem,tree);
748 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
750 std::vector<CShadowTree*> selectedLeafs;
751 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
752 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
753 while(pos)
755 selectedLeafs.push_back(
756 (CShadowTree*)m_ListRefLeafs.GetItemData(
757 m_ListRefLeafs.GetNextSelectedItem(pos)));
760 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
763 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
765 CIconMenu popupMenu;
766 popupMenu.CreatePopupMenu();
768 bool bAddSeparator = false;
769 CString remoteName;
771 if(selectedLeafs.size()==1)
773 bAddSeparator = true;
775 bool bShowReflogOption = false;
776 bool bShowFetchOption = false;
777 bool bShowSwitchOption = false;
778 bool bShowRenameOption = false;
779 bool bShowCreateBranchOption = false;
780 bool bShowEditBranchDescriptionOption = false;
782 CString fetchFromCmd;
784 if(selectedLeafs[0]->IsFrom(L"refs/heads"))
786 bShowReflogOption = true;
787 bShowSwitchOption = true;
788 bShowRenameOption = true;
789 bShowEditBranchDescriptionOption = true;
791 else if(selectedLeafs[0]->IsFrom(L"refs/remotes"))
793 bShowReflogOption = true;
794 bShowFetchOption = true;
795 bShowCreateBranchOption = true;
797 int dummy = 0;//Needed for tokenize
798 remoteName = selectedLeafs[0]->GetRefName();
799 remoteName = remoteName.Mid(13);
800 remoteName = remoteName.Tokenize(L"/", dummy);
801 fetchFromCmd.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
803 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))
807 CString temp;
808 temp.LoadString(IDS_MENULOG);
809 popupMenu.AppendMenuIcon(eCmd_ViewLog, temp, IDI_LOG);
810 popupMenu.AppendMenuIcon(eCmd_RepoBrowser, IDS_LOG_BROWSEREPO, IDI_REPOBROWSE);
811 if(bShowReflogOption)
813 temp.LoadString(IDS_MENUREFLOG);
814 popupMenu.AppendMenuIcon(eCmd_ShowReflog, temp, IDI_LOG);
817 popupMenu.AppendMenu(MF_SEPARATOR);
818 bAddSeparator = false;
820 if(bShowFetchOption)
822 bAddSeparator = true;
823 popupMenu.AppendMenuIcon(eCmd_Fetch, fetchFromCmd, IDI_PULL);
826 if(bAddSeparator)
827 popupMenu.AppendMenu(MF_SEPARATOR);
829 bAddSeparator = false;
830 if (m_bHasWC)
832 popupMenu.AppendMenuIcon(eCmd_Switch, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS)), IDI_SWITCH);
833 popupMenu.AppendMenu(MF_SEPARATOR);
836 if(bShowCreateBranchOption)
838 bAddSeparator = true;
839 temp.LoadString(IDS_MENUBRANCH);
840 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
843 if (bShowEditBranchDescriptionOption)
845 bAddSeparator = true;
846 popupMenu.AppendMenuIcon(eCmd_EditBranchDescription, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION)), IDI_RENAME);
848 if(bShowRenameOption)
850 bAddSeparator = true;
851 popupMenu.AppendMenuIcon(eCmd_Rename, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME)), IDI_RENAME);
854 else if(selectedLeafs.size() == 2)
856 bAddSeparator = true;
857 popupMenu.AppendMenuIcon(eCmd_Diff, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS)), IDI_DIFF);
860 if(!selectedLeafs.empty())
862 if(AreAllFrom(selectedLeafs, L"refs/remotes/"))
864 if(bAddSeparator)
865 popupMenu.AppendMenu(MF_SEPARATOR);
866 CString menuItemName;
867 if(selectedLeafs.size() == 1)
868 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH);
869 else
870 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES, selectedLeafs.size());
872 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteBranch, menuItemName, IDI_DELETE);
873 bAddSeparator = true;
875 else if(AreAllFrom(selectedLeafs, L"refs/heads/"))
877 if(bAddSeparator)
878 popupMenu.AppendMenu(MF_SEPARATOR);
879 CString menuItemName;
880 if(selectedLeafs.size() == 1)
881 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH);
882 else
883 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES, selectedLeafs.size());
885 popupMenu.AppendMenuIcon(eCmd_DeleteBranch, menuItemName, IDI_DELETE);
886 bAddSeparator = true;
888 else if(AreAllFrom(selectedLeafs, L"refs/tags/"))
890 if(bAddSeparator)
891 popupMenu.AppendMenu(MF_SEPARATOR);
892 CString menuItemName;
893 if(selectedLeafs.size() == 1)
894 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETETAG);
895 else
896 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETETAGS, selectedLeafs.size());
898 popupMenu.AppendMenuIcon(eCmd_DeleteTag, menuItemName, IDI_DELETE);
899 bAddSeparator = true;
904 if(hTreePos!=NULL && selectedLeafs.empty())
906 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);
907 if(pTree->IsFrom(L"refs/remotes"))
909 if(bAddSeparator)
910 popupMenu.AppendMenu(MF_SEPARATOR);
911 popupMenu.AppendMenuIcon(eCmd_ManageRemotes, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES)), IDI_SETTINGS);
912 bAddSeparator = true;
913 if(selectedLeafs.empty())
915 int dummy = 0;//Needed for tokenize
916 remoteName = pTree->GetRefName();
917 remoteName = remoteName.Mid(13);
918 remoteName = remoteName.Tokenize(L"/", dummy);
919 if(!remoteName.IsEmpty())
921 CString temp;
922 temp.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
923 popupMenu.AppendMenuIcon(eCmd_Fetch, temp, IDI_PULL);
925 temp.LoadString(IDS_DELETEREMOTETAG);
926 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteTag, temp, IDI_DELETE);
930 if(pTree->IsFrom(L"refs/heads"))
932 if(bAddSeparator)
933 popupMenu.AppendMenu(MF_SEPARATOR);
934 CString temp;
935 temp.LoadString(IDS_MENUBRANCH);
936 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
938 if(pTree->IsFrom(L"refs/tags"))
940 if(bAddSeparator)
941 popupMenu.AppendMenu(MF_SEPARATOR);
942 CString temp;
943 temp.LoadString(IDS_MENUTAG);
944 popupMenu.AppendMenuIcon(eCmd_CreateTag, temp, IDI_TAG);
945 temp.LoadString(IDS_PROC_BROWSEREFS_DELETEALLTAGS);
946 popupMenu.AppendMenuIcon(eCmd_DeleteAllTags, temp, IDI_DELETE);
951 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
952 switch(cmd)
954 case eCmd_ViewLog:
956 CLogDlg dlg;
957 dlg.SetStartRef(selectedLeafs[0]->GetRefName());
958 dlg.DoModal();
960 break;
961 case eCmd_RepoBrowser:
962 CAppUtils::RunTortoiseGitProc(_T("/command:repobrowser /path:\"") + g_Git.m_CurrentDir + _T("\" /rev:") + selectedLeafs[0]->GetRefName());
963 break;
964 case eCmd_DeleteBranch:
965 case eCmd_DeleteRemoteBranch:
967 if(ConfirmDeleteRef(selectedLeafs))
968 DoDeleteRefs(selectedLeafs, true);
969 Refresh();
971 break;
972 case eCmd_DeleteTag:
974 if(ConfirmDeleteRef(selectedLeafs))
975 DoDeleteRefs(selectedLeafs, true);
976 Refresh();
978 break;
979 case eCmd_ShowReflog:
981 CRefLogDlg refLogDlg(this);
982 refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();
983 refLogDlg.DoModal();
985 break;
986 case eCmd_Fetch:
988 CAppUtils::Fetch(remoteName);
989 Refresh();
991 break;
992 case eCmd_DeleteRemoteTag:
994 CDeleteRemoteTagDlg deleteRemoteTagDlg;
995 deleteRemoteTagDlg.m_sRemote = remoteName;
996 deleteRemoteTagDlg.DoModal();
998 break;
999 case eCmd_Switch:
1001 CAppUtils::Switch(selectedLeafs[0]->GetRefName());
1003 break;
1004 case eCmd_Rename:
1006 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1007 if(pos != NULL)
1008 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1010 break;
1011 case eCmd_AddRemote:
1013 CAddRemoteDlg(this).DoModal();
1014 Refresh();
1016 break;
1017 case eCmd_ManageRemotes:
1019 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS)), new CSettingGitRemote(g_Git.m_CurrentDir), this).DoModal();
1020 // CSettingGitRemote W_Remotes(m_cmdPath);
1021 // W_Remotes.DoModal();
1022 Refresh();
1024 break;
1025 case eCmd_CreateBranch:
1027 CString *commitHash = NULL;
1028 if (selectedLeafs.size() == 1)
1029 commitHash = &(selectedLeafs[0]->m_csRefHash);
1030 CAppUtils::CreateBranchTag(false, commitHash);
1031 Refresh();
1033 break;
1034 case eCmd_CreateTag:
1036 CAppUtils::CreateBranchTag(true);
1037 Refresh();
1039 break;
1040 case eCmd_DeleteAllTags:
1042 for (int i = 0; i < m_ListRefLeafs.GetItemCount(); ++i)
1044 m_ListRefLeafs.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
1045 selectedLeafs.push_back((CShadowTree*)m_ListRefLeafs.GetItemData(i));
1047 if (ConfirmDeleteRef(selectedLeafs))
1048 DoDeleteRefs(selectedLeafs, true);
1049 Refresh();
1051 break;
1052 case eCmd_Diff:
1054 CFileDiffDlg dlg;
1055 dlg.SetDiff(
1056 NULL,
1057 selectedLeafs[0]->m_csRefHash,
1058 selectedLeafs[1]->m_csRefHash);
1059 dlg.DoModal();
1061 break;
1062 case eCmd_EditBranchDescription:
1064 CInputDlg dlg;
1065 dlg.m_sHintText = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1066 dlg.m_sInputText = selectedLeafs[0]->m_csDescription;
1067 dlg.m_sTitle = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1068 dlg.m_bUseLogWidth = true;
1069 if(dlg.DoModal() == IDOK)
1071 CString key;
1072 key.Format(_T("branch.%s.description"), selectedLeafs[0]->m_csRefName);
1073 dlg.m_sInputText.Replace(_T("\r"), _T(""));
1074 dlg.m_sInputText.Trim();
1075 if (dlg.m_sInputText.IsEmpty())
1076 g_Git.UnsetConfigValue(key);
1077 else
1078 g_Git.SetConfigValue(key, dlg.m_sInputText);
1079 Refresh();
1082 break;
1086 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree& leafs, const wchar_t* from)
1088 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
1089 if(!(*i)->IsFrom(from))
1090 return false;
1091 return true;
1094 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
1096 if (pMsg->message == WM_KEYDOWN)
1098 switch (pMsg->wParam)
1100 /* case VK_RETURN:
1102 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1104 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1106 PostMessage(WM_COMMAND, IDOK);
1108 return TRUE;
1111 break;
1112 */ case VK_F2:
1114 if(pMsg->hwnd == m_ListRefLeafs.m_hWnd)
1116 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1117 if(pos != NULL)
1118 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1121 break;
1123 case VK_F5:
1125 Refresh();
1127 break;
1132 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
1135 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1137 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1138 *pResult = 0;
1140 if(m_currSortCol == pNMLV->iSubItem)
1141 m_currSortDesc = !m_currSortDesc;
1142 else
1144 m_currSortCol = pNMLV->iSubItem;
1145 m_currSortDesc = false;
1148 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
1149 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
1151 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
1154 void CBrowseRefsDlg::OnDestroy()
1156 m_pickedRef = GetSelectedRef(true, false);
1158 CResizableStandAloneDialog::OnDestroy();
1161 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR * /*pNMHDR*/, LRESULT *pResult)
1163 *pResult = 0;
1165 EndDialog(IDOK);
1168 CString CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef, int pickRef_Kind)
1170 CBrowseRefsDlg dlg(CString(),NULL);
1172 if(initialRef.IsEmpty())
1173 initialRef = L"HEAD";
1174 dlg.m_initialRef = initialRef;
1175 dlg.m_pickRef_Kind = pickRef_Kind;
1176 dlg.m_bPickOne = true;
1178 if(dlg.DoModal() != IDOK)
1179 return CString();
1181 return dlg.m_pickedRef;
1184 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)
1186 CString origRef;
1187 pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);
1188 CString resultRef = PickRef(false,origRef,pickRef_Kind);
1189 if(resultRef.IsEmpty())
1190 return false;
1191 if(wcsncmp(resultRef,L"refs/",5)==0)
1192 resultRef = resultRef.Mid(5);
1193 // if(wcsncmp(resultRef,L"heads/",6)==0)
1194 // resultRef = resultRef.Mid(6);
1196 //Find closest match of choice in combobox
1197 int ixFound = -1;
1198 int matchLength = 0;
1199 CString comboRefName;
1200 for(int i = 0; i < pComboBox->GetCount(); ++i)
1202 pComboBox->GetLBText(i, comboRefName);
1203 if(comboRefName.Find(L'/') < 0 && !comboRefName.IsEmpty())
1204 comboRefName.Insert(0,L"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1205 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)
1207 matchLength = comboRefName.GetLength();
1208 ixFound = i;
1211 if(ixFound >= 0)
1212 pComboBox->SetCurSel(ixFound);
1213 else
1214 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)
1216 return true;
1219 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1221 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1222 *pResult = FALSE;
1224 if(pDispInfo->item.pszText == NULL)
1225 return; //User canceled changing
1227 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1229 if(!pTree->IsFrom(L"refs/heads"))
1231 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES, IDS_APPNAME, MB_OK | MB_ICONERROR);
1232 return;
1235 CString origName = pTree->GetRefName().Mid(11);
1237 CString newName;
1238 if(m_pListCtrlRoot != NULL)
1239 newName = m_pListCtrlRoot->GetRefName() + L'/';
1240 newName += pDispInfo->item.pszText;
1242 if(wcsncmp(newName,L"refs/heads/",11)!=0)
1244 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE, IDS_APPNAME, MB_OK | MB_ICONERROR);
1245 return;
1248 CString newNameTrunced = newName.Mid(11);
1250 CString errorMsg;
1251 if(g_Git.Run(L"git branch -m \"" + origName + L"\" \"" + newNameTrunced + L"\"", &errorMsg, CP_UTF8) != 0)
1253 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1254 return;
1256 //Do as if it failed to rename. Let Refresh() do the job.
1257 //*pResult = TRUE;
1259 Refresh(newName);
1261 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1263 // AfxMessageBox(W_csPopup);
1267 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1269 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1270 *pResult = FALSE;
1272 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1274 if(!pTree->IsFrom(L"refs/heads"))
1276 *pResult = TRUE; //Dont allow renaming any other things then branches at the moment.
1277 return;
1281 void CBrowseRefsDlg::OnEnChangeEditFilter()
1283 SetTimer(IDT_FILTER, 1000, NULL);
1286 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent)
1288 if (nIDEvent == IDT_FILTER)
1290 KillTimer(IDT_FILTER);
1291 FillListCtrlForTreeNode(m_RefTreeCtrl.GetSelectedItem());
1294 CResizableStandAloneDialog::OnTimer(nIDEvent);
1297 LRESULT CBrowseRefsDlg::OnClickedInfoIcon(WPARAM /*wParam*/, LPARAM lParam)
1299 // FIXME: x64 version would get this function called with unexpected parameters.
1300 if (!lParam)
1301 return 0;
1303 RECT * rect = (LPRECT)lParam;
1304 CPoint point;
1305 CString temp;
1306 point = CPoint(rect->left, rect->bottom);
1307 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1308 CMenu popup;
1309 if (popup.CreatePopupMenu())
1311 temp.LoadString(IDS_LOG_FILTER_REFNAME);
1312 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME), LOGFILTER_REFNAME, temp);
1314 temp.LoadString(IDS_LOG_FILTER_SUBJECT);
1315 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT), LOGFILTER_SUBJECT, temp);
1317 temp.LoadString(IDS_LOG_FILTER_AUTHORS);
1318 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS), LOGFILTER_AUTHORS, temp);
1320 temp.LoadString(IDS_LOG_FILTER_REVS);
1321 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS), LOGFILTER_REVS, temp);
1323 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1324 if (selection != 0)
1326 m_SelectedFilters ^= selection;
1327 SetFilterCueText();
1328 SetTimer(IDT_FILTER, 1000, NULL);
1331 return 0L;
1334 void CBrowseRefsDlg::SetFilterCueText()
1336 CString temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY));
1337 temp += _T(" ");
1339 if (m_SelectedFilters & LOGFILTER_REFNAME)
1340 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME));
1342 if (m_SelectedFilters & LOGFILTER_SUBJECT)
1344 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1345 temp += _T(", ");
1346 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT));
1349 if (m_SelectedFilters & LOGFILTER_AUTHORS)
1351 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1352 temp += _T(", ");
1353 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS));
1356 if (m_SelectedFilters & LOGFILTER_REVS)
1358 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1359 temp += _T(", ");
1360 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS));
1363 // to make the cue banner text appear more to the right of the edit control
1364 temp = _T(" ") + temp;
1365 m_ctrlFilter.SetCueBanner(temp.TrimRight());