Allow to use libgit2 for unified diff
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blob971d1d733b6af890cd3fba0ef6f73a4927b7d80c
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"
37 #include "SysProgressDlg.h"
39 static int SplitRemoteBranchName(CString ref, CString &remote, CString &branch)
41 if (ref.Left(13) == _T("refs/remotes/"))
42 ref = ref.Mid(13);
43 else if (ref.Left(8) == _T("remotes/"))
44 ref = ref.Mid(8);
46 STRING_VECTOR list;
47 int result = g_Git.GetRemoteList(list);
48 if (result != 0)
49 return result;
51 for (size_t i = 0; i < list.size(); ++i)
53 if (ref.Left(list[i].GetLength() + 1) == list[i] + _T("/"))
55 remote = list[i];
56 branch = ref.Mid(list[i].GetLength() + 1);
57 return 0;
59 if (ref == list[i])
61 remote = list[i];
62 branch = _T("");
63 return 0;
67 return -1;
70 void SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)
72 if (control == NULL)
73 return;
74 // set the sort arrow
75 CHeaderCtrl * pHeader = control->GetHeaderCtrl();
76 HDITEM HeaderItem = {0};
77 HeaderItem.mask = HDI_FORMAT;
78 for (int i=0; i<pHeader->GetItemCount(); ++i)
80 pHeader->GetItem(i, &HeaderItem);
81 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
82 pHeader->SetItem(i, &HeaderItem);
84 if (nColumn >= 0)
86 pHeader->GetItem(nColumn, &HeaderItem);
87 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);
88 pHeader->SetItem(nColumn, &HeaderItem);
92 class CRefLeafListCompareFunc
94 public:
95 CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){
96 m_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER);
97 if (m_bSortLogical)
98 m_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE);
101 static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
103 return ((CRefLeafListCompareFunc*)lParamSort)->Compare(lParam1,lParam2);
106 int Compare(LPARAM lParam1, LPARAM lParam2)
108 return Compare(
109 (CShadowTree*)m_pList->GetItemData((int)lParam1),
110 (CShadowTree*)m_pList->GetItemData((int)lParam2));
113 int Compare(CShadowTree* pLeft, CShadowTree* pRight)
115 int result=CompareNoDesc(pLeft,pRight);
116 if(m_desc)
117 return -result;
118 return result;
121 int CompareNoDesc(CShadowTree* pLeft, CShadowTree* pRight)
123 switch(m_col)
125 case CBrowseRefsDlg::eCol_Name: return SortStrCmp(pLeft->GetRefName(), pRight->GetRefName());
126 case CBrowseRefsDlg::eCol_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);
127 case CBrowseRefsDlg::eCol_Msg: return SortStrCmp(pLeft->m_csSubject, pRight->m_csSubject);
128 case CBrowseRefsDlg::eCol_LastAuthor: return SortStrCmp(pLeft->m_csAuthor, pRight->m_csAuthor);
129 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);
130 case CBrowseRefsDlg::eCol_Description: return SortStrCmp(pLeft->m_csDescription, pRight->m_csDescription);
132 return 0;
134 int SortStrCmp(CString left, CString right)
136 if (m_bSortLogical)
137 return StrCmpLogicalW(left, right);
138 return StrCmpI(left, right);
141 int m_col;
142 bool m_desc;
143 CListCtrl* m_pList;
144 bool m_bSortLogical;
147 // CBrowseRefsDlg dialog
149 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)
151 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)
152 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),
153 m_cmdPath(cmdPath),
154 m_currSortCol(0),
155 m_currSortDesc(false),
156 m_initialRef(L"HEAD"),
157 m_pickRef_Kind(gPickRef_All),
158 m_pListCtrlRoot(NULL),
159 m_bHasWC(true),
160 m_SelectedFilters(LOGFILTER_ALL),
161 m_bPickOne(false)
166 CBrowseRefsDlg::~CBrowseRefsDlg()
170 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)
172 CDialog::DoDataExchange(pDX);
173 DDX_Control(pDX, IDC_TREE_REF, m_RefTreeCtrl);
174 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_ListRefLeafs);
175 DDX_Control(pDX, IDC_BROWSEREFS_EDIT_FILTER, m_ctrlFilter);
179 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)
180 ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)
181 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)
182 ON_WM_CONTEXTMENU()
183 ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)
184 ON_WM_DESTROY()
185 ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)
186 ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnItemChangedListRefLeafs)
187 ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs)
188 ON_NOTIFY(LVN_BEGINLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs)
189 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER, &CBrowseRefsDlg::OnEnChangeEditFilter)
190 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED, OnClickedInfoIcon)
191 ON_WM_TIMER()
192 END_MESSAGE_MAP()
195 // CBrowseRefsDlg message handlers
197 void CBrowseRefsDlg::OnBnClickedOk()
199 OnOK();
202 BOOL CBrowseRefsDlg::OnInitDialog()
204 CResizableStandAloneDialog::OnInitDialog();
205 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
207 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
208 m_ctrlFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
209 m_ctrlFilter.SetInfoIcon(IDI_FILTEREDIT);
210 SetFilterCueText();
212 AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);
213 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
214 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER, TOP_LEFT);
215 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER, TOP_LEFT, TOP_RIGHT);
216 AddAnchor(IDHELP, BOTTOM_RIGHT);
218 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
219 CString temp;
220 temp.LoadString(IDS_BRANCHNAME);
221 m_ListRefLeafs.InsertColumn(eCol_Name, temp, 0, 150);
222 temp.LoadString(IDS_DATELASTCOMMIT);
223 m_ListRefLeafs.InsertColumn(eCol_Date, temp, 0, 100);
224 temp.LoadString(IDS_LASTCOMMIT);
225 m_ListRefLeafs.InsertColumn(eCol_Msg, temp, 0, 300);
226 temp.LoadString(IDS_LASTAUTHOR);
227 m_ListRefLeafs.InsertColumn(eCol_LastAuthor, temp, 0, 100);
228 temp.LoadString(IDS_HASH);
229 m_ListRefLeafs.InsertColumn(eCol_Hash, temp, 0, 80);
230 temp.LoadString(IDS_DESCRIPTION);
231 m_ListRefLeafs.InsertColumn(eCol_Description, temp, 0, 80);
233 AddAnchor(IDOK,BOTTOM_RIGHT);
234 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
236 Refresh(m_initialRef);
238 EnableSaveRestore(L"BrowseRefs");
240 CString sWindowTitle;
241 GetWindowText(sWindowTitle);
242 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
244 m_bHasWC = !g_GitAdminDir.IsBareRepo(g_Git.m_CurrentDir);
246 if (m_bPickOne)
247 m_ListRefLeafs.ModifyStyle(0, LVS_SINGLESEL);
249 m_ListRefLeafs.SetFocus();
250 return FALSE;
253 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
255 int posSlash=nameLeft.Find('/');
256 CString nameSub;
257 if(posSlash<0)
259 nameSub=nameLeft;
260 nameLeft.Empty();//Nothing left
262 else
264 nameSub=nameLeft.Left(posSlash);
265 nameLeft=nameLeft.Mid(posSlash+1);
267 if(nameSub.IsEmpty())
268 return NULL;
270 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
271 return NULL;
273 CShadowTree& nextNode=m_ShadowTree[nameSub];
274 nextNode.m_csRefName=nameSub;
275 nextNode.m_pParent=this;
276 return &nextNode;
279 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)
281 if(IsLeaf())
283 if(m_csRefName.GetLength() > partialRefName.GetLength())
284 return NULL;
285 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)
287 //Match of leaf name. Try match on total name.
288 CString totalRefName = GetRefName();
289 if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)
290 return this; //Also match. Found.
293 else
295 //Not a leaf. Search all nodes.
296 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)
298 CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);
299 if(pSubtree != NULL)
300 return pSubtree; //Found
303 return NULL;//Not found
307 typedef std::map<CString,CString> MAP_STRING_STRING;
309 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf, bool pickFirstSelIfMultiSel)
311 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
312 //List ctrl selection?
313 if(pos && (pickFirstSelIfMultiSel || m_ListRefLeafs.GetSelectedCount() == 1))
315 //A leaf is selected
316 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(
317 m_ListRefLeafs.GetNextSelectedItem(pos));
318 return pTree->GetRefName();
320 else if (pos && !pickFirstSelIfMultiSel)
322 // at least one leaf is selected
323 CString refs;
324 int index;
325 while ((index = m_ListRefLeafs.GetNextSelectedItem(pos)) >= 0)
327 CString ref = ((CShadowTree*)m_ListRefLeafs.GetItemData(index))->GetRefName();
328 if(wcsncmp(ref, L"refs/", 5) == 0)
329 ref = ref.Mid(5);
330 if(wcsncmp(ref, L"heads/", 6) == 0)
331 ref = ref.Mid(6);
332 refs += ref + _T(" ");
334 return refs.Trim();
336 else if(!onlyIfLeaf)
338 //Tree ctrl selection?
339 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
340 if(hTree!=NULL)
342 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);
343 return pTree->GetRefName();
346 return CString();//None
349 static int GetBranchDescriptionsCallback(const git_config_entry *entry, void *data)
351 MAP_STRING_STRING *descriptions = (MAP_STRING_STRING *) data;
352 CString key = CUnicodeUtils::GetUnicode(entry->name, CP_UTF8);
353 CString val = CUnicodeUtils::GetUnicode(entry->value, CP_UTF8);
354 descriptions->insert(make_pair(key.Mid(7, key.GetLength() - 7 - 12), val)); // 7: branch., 12: .description
355 return 0;
358 MAP_STRING_STRING GetBranchDescriptions()
360 MAP_STRING_STRING descriptions;
361 git_config * config;
362 git_config_new(&config);
363 CStringA projectConfigA = CUnicodeUtils::GetMulti(g_Git.GetGitLocalConfig(), CP_UTF8);
364 git_config_add_file_ondisk(config, projectConfigA.GetBuffer(), 3, FALSE);
365 projectConfigA.ReleaseBuffer();
366 git_config_foreach_match(config, "branch\\..*\\.description", GetBranchDescriptionsCallback, &descriptions);
367 git_config_free(config);
368 return descriptions;
371 void CBrowseRefsDlg::Refresh(CString selectRef)
373 // m_RefMap.clear();
374 // g_Git.GetMapHashToFriendName(m_RefMap);
376 if(!selectRef.IsEmpty())
378 if(selectRef == "HEAD")
380 selectRef = g_Git.GetSymbolicRef(selectRef, false);
383 else
385 selectRef = GetSelectedRef(false, true);
388 m_RefTreeCtrl.DeleteAllItems();
389 m_ListRefLeafs.DeleteAllItems();
390 m_TreeRoot.m_ShadowTree.clear();
391 m_TreeRoot.m_csRefName = "refs";
392 m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);
393 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);
395 CString allRefs, error;
396 if (g_Git.Run(L"git for-each-ref --format="
397 L"%(refname)%04"
398 L"%(objectname)%04"
399 L"%(authordate:relative)%04"
400 L"%(subject)%04"
401 L"%(authorname)%04"
402 L"%(authordate:iso8601)%03",
403 &allRefs, &error, CP_UTF8))
405 CMessageBox::Show(NULL, CString(_T("Get refs failed\n")) + error, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
408 int linePos=0;
409 CString singleRef;
411 MAP_STRING_STRING refMap;
413 //First sort on ref name
414 while(!(singleRef=allRefs.Tokenize(L"\03",linePos)).IsEmpty())
416 singleRef.TrimLeft(L"\r\n");
417 int valuePos=0;
418 CString refName=singleRef.Tokenize(L"\04",valuePos);
419 if(refName.IsEmpty())
420 continue;
421 CString refRest=singleRef.Mid(valuePos);
424 //Use ref based on m_pickRef_Kind
425 if(wcsncmp(refName,L"refs/heads",10)==0 && !(m_pickRef_Kind & gPickRef_Head) )
426 continue; //Skip
427 if(wcsncmp(refName,L"refs/tags",9)==0 && !(m_pickRef_Kind & gPickRef_Tag) )
428 continue; //Skip
429 if(wcsncmp(refName,L"refs/remotes",12)==0 && !(m_pickRef_Kind & gPickRef_Remote) )
430 continue; //Skip
432 refMap[refName] = refRest; //Use
435 MAP_STRING_STRING descriptions = GetBranchDescriptions();
437 //Populate ref tree
438 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
440 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
441 CString values=iterRefMap->second;
442 values.Replace(L"\04" L"\04",L"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
444 int valuePos=0;
445 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
446 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
447 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
448 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
449 treeLeaf.m_csDate_Iso8601= values.Tokenize(L"\04",valuePos);
451 if (wcsncmp(iterRefMap->first, L"refs/heads", 10) == 0)
452 treeLeaf.m_csDescription = descriptions[treeLeaf.m_csRefName];
456 if(selectRef.IsEmpty() || !SelectRef(selectRef, false))
457 //Probably not on a branch. Select root node.
458 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);
462 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
464 if(!bExactMatch)
466 CString newRefName = GetFullRefName(refName);
467 if(!newRefName.IsEmpty())
468 refName = newRefName;
469 //else refName is not a valid ref. Try to select as good as possible.
471 if(_wcsnicmp(refName, L"refs/", 5) != 0)
472 return false; // Not a ref name
474 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
475 if(treeLeafHead.m_hTree != NULL)
477 //Not a leaf. Select tree node and return
478 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
479 return true;
482 if(treeLeafHead.m_pParent==NULL)
483 return false; //Weird... should not occur.
485 //This is the current head.
486 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
488 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
490 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
491 if(pCurrShadowTree == &treeLeafHead)
493 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
494 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
498 return true;
501 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
503 if(pTreePos==NULL)
505 if(_wcsnicmp(refName, L"refs/", 5) == 0)
506 refName=refName.Mid(5);
507 pTreePos=&m_TreeRoot;
509 if(refName.IsEmpty())
510 return *pTreePos;//Found leaf
512 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
513 if(pNextTree==NULL)
515 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
516 ASSERT(!bCreateIfNotExist);
517 return *pTreePos;
520 if(!refName.IsEmpty())
522 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
523 //Leafs are for the list control.
524 if(pNextTree->m_hTree==NULL)
526 //New tree. Create node in control.
527 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
528 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
532 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
536 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
538 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
539 *pResult = 0;
541 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
544 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
546 m_ListRefLeafs.DeleteAllItems();
548 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
549 if(pTree==NULL)
551 ASSERT(FALSE);
552 return;
554 FillListCtrlForShadowTree(pTree,L"",true);
557 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
559 if(pTree->IsLeaf())
561 CString filter;
562 m_ctrlFilter.GetWindowText(filter);
563 filter.MakeLower();
564 CString ref = refNamePrefix + pTree->m_csRefName;
565 if (!(pTree->m_csRefName.IsEmpty() || pTree->m_csRefName == "refs" && pTree->m_pParent == NULL) && IsMatchFilter(pTree, ref, filter))
567 int indexItem = m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(), L"");
569 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
570 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, ref);
571 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);
572 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
573 m_ListRefLeafs.SetItemText(indexItem,eCol_LastAuthor, pTree->m_csAuthor);
574 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
575 int pos = 0;
576 m_ListRefLeafs.SetItemText(indexItem,eCol_Description, pTree->m_csDescription.Tokenize(_T("\n"), pos));
579 else
582 CString csThisName;
583 if(!isFirstLevel)
584 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
585 else
586 m_pListCtrlRoot = pTree;
587 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
589 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
592 if (isFirstLevel)
594 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
595 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
597 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
601 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree* pTree, const CString &ref, const CString &filter)
603 if (m_SelectedFilters & LOGFILTER_REFNAME)
605 CString msg = ref;
606 msg = msg.MakeLower();
608 if (msg.Find(filter) >= 0)
609 return true;
612 if (m_SelectedFilters & LOGFILTER_SUBJECT)
614 CString msg = pTree->m_csSubject;
615 msg = msg.MakeLower();
617 if (msg.Find(filter) >= 0)
618 return true;
621 if (m_SelectedFilters & LOGFILTER_AUTHORS)
623 CString msg = pTree->m_csAuthor;
624 msg = msg.MakeLower();
626 if (msg.Find(filter) >= 0)
627 return true;
630 if (m_SelectedFilters & LOGFILTER_REVS)
632 CString msg = pTree->m_csRefHash;
633 msg = msg.MakeLower();
635 if (msg.Find(filter) >= 0)
636 return true;
638 return false;
641 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree& leafs)
643 ASSERT(!leafs.empty());
645 CString csMessage;
646 UINT mbIcon=MB_ICONQUESTION;
648 bool bIsRemoteBranch = false;
649 bool bIsBranch = false;
650 if (leafs[0]->IsFrom(L"refs/remotes")) {bIsBranch = true; bIsRemoteBranch = true;}
651 else if (leafs[0]->IsFrom(L"refs/heads")) {bIsBranch = true;}
653 if(bIsBranch)
655 if(leafs.size() == 1)
657 CString branchToDelete = leafs[0]->GetRefName().Mid(bIsRemoteBranch ? 13 : 11);
658 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, branchToDelete);
660 //Check if branch is fully merged in HEAD
661 if (!g_Git.IsFastForward(leafs[0]->GetRefName(), _T("HEAD")))
663 csMessage += L"\r\n\r\n";
664 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED));
665 mbIcon = MB_ICONWARNING;
668 if(bIsRemoteBranch)
670 csMessage += L"\r\n\r\n";
671 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
672 mbIcon = MB_ICONWARNING;
675 else
677 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
679 csMessage += L"\r\n\r\n";
680 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK));
681 mbIcon = MB_ICONWARNING;
683 if(bIsRemoteBranch)
685 csMessage += L"\r\n\r\n";
686 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
687 mbIcon = MB_ICONWARNING;
692 else if(leafs[0]->IsFrom(L"refs/tags"))
694 if(leafs.size() == 1)
696 CString tagToDelete = leafs[0]->GetRefName().Mid(10);
697 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, tagToDelete);
699 else
701 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
705 return CMessageBox::Show(m_hWnd, csMessage, _T("TortoiseGit"), MB_YESNO | mbIcon) == IDYES;
709 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree& leafs, bool bForce)
711 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
712 if(!DoDeleteRef((*i)->GetRefName(), bForce))
713 return false;
714 return true;
717 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)
719 bool bIsRemoteBranch = false;
720 bool bIsBranch = false;
721 if (wcsncmp(completeRefName, L"refs/remotes",12)==0) {bIsBranch = true; bIsRemoteBranch = true;}
722 else if (wcsncmp(completeRefName, L"refs/heads",10)==0) {bIsBranch = true;}
724 if(bIsBranch)
726 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);
727 CString cmd;
728 if(bIsRemoteBranch)
730 CString remoteName, remoteBranchToDelete;
731 if (SplitRemoteBranchName(branchToDelete, remoteName, remoteBranchToDelete))
732 return false;
734 if(CAppUtils::IsSSHPutty())
736 CAppUtils::LaunchPAgent(NULL, &remoteName);
739 cmd.Format(L"git.exe push \"%s\" :refs/heads/%s", remoteName, remoteBranchToDelete);
741 else
742 cmd.Format(L"git.exe branch -%c -- %s",bForce?L'D':L'd',branchToDelete);
743 CSysProgressDlg sysProgressDlg;
744 if (bIsRemoteBranch)
746 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
747 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS)));
748 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
749 sysProgressDlg.SetShowProgressBar(false);
750 sysProgressDlg.ShowModal(this, true);
752 CString errorMsg;
753 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
755 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
756 if (bIsRemoteBranch)
757 sysProgressDlg.Stop();
758 return false;
760 if (bIsRemoteBranch)
761 sysProgressDlg.Stop();
763 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
765 CString tagToDelete = completeRefName.Mid(10);
766 CString cmd;
767 cmd.Format(L"git.exe tag -d -- %s",tagToDelete);
768 CString errorMsg;
769 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
771 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
772 return false;
775 return true;
778 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
780 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
781 if(pLeaf == NULL)
782 return CString();
783 return pLeaf->GetRefName();
787 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
789 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
790 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
793 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
795 CPoint clientPoint=point;
796 m_RefTreeCtrl.ScreenToClient(&clientPoint);
798 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
799 if(hTreeItem!=NULL)
800 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
802 VectorPShadowTree tree;
803 ShowContextMenu(point,hTreeItem,tree);
807 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
809 std::vector<CShadowTree*> selectedLeafs;
810 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
811 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
812 while(pos)
814 selectedLeafs.push_back(
815 (CShadowTree*)m_ListRefLeafs.GetItemData(
816 m_ListRefLeafs.GetNextSelectedItem(pos)));
819 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
822 CString GetTwoSelectedRefs(VectorPShadowTree& selectedLeafs, const CString &lastSelected, const CString &separator)
824 ASSERT(selectedLeafs.size() == 2);
826 if (selectedLeafs.at(0)->GetRefName() == lastSelected)
827 return g_Git.StripRefName(selectedLeafs.at(1)->GetRefName()) + separator + g_Git.StripRefName(lastSelected);
828 else
829 return g_Git.StripRefName(selectedLeafs.at(0)->GetRefName()) + separator + g_Git.StripRefName(lastSelected);
832 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
834 CIconMenu popupMenu;
835 popupMenu.CreatePopupMenu();
837 bool bAddSeparator = false;
838 CString remoteName;
840 if(selectedLeafs.size()==1)
842 bAddSeparator = true;
844 bool bShowReflogOption = false;
845 bool bShowFetchOption = false;
846 bool bShowSwitchOption = false;
847 bool bShowRenameOption = false;
848 bool bShowCreateBranchOption = false;
849 bool bShowEditBranchDescriptionOption = false;
851 CString fetchFromCmd;
853 if(selectedLeafs[0]->IsFrom(L"refs/heads"))
855 bShowReflogOption = true;
856 bShowSwitchOption = true;
857 bShowRenameOption = true;
858 bShowEditBranchDescriptionOption = true;
860 else if(selectedLeafs[0]->IsFrom(L"refs/remotes"))
862 bShowReflogOption = true;
863 bShowFetchOption = true;
864 bShowCreateBranchOption = true;
866 CString remoteBranch;
867 if (SplitRemoteBranchName(selectedLeafs[0]->GetRefName(), remoteName, remoteBranch))
868 bShowFetchOption = false;
869 else
870 fetchFromCmd.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
872 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))
876 CString temp;
877 temp.LoadString(IDS_MENULOG);
878 popupMenu.AppendMenuIcon(eCmd_ViewLog, temp, IDI_LOG);
879 popupMenu.AppendMenuIcon(eCmd_RepoBrowser, IDS_LOG_BROWSEREPO, IDI_REPOBROWSE);
880 if(bShowReflogOption)
882 temp.LoadString(IDS_MENUREFLOG);
883 popupMenu.AppendMenuIcon(eCmd_ShowReflog, temp, IDI_LOG);
886 popupMenu.AppendMenu(MF_SEPARATOR);
887 bAddSeparator = false;
889 if(bShowFetchOption)
891 bAddSeparator = true;
892 popupMenu.AppendMenuIcon(eCmd_Fetch, fetchFromCmd, IDI_PULL);
895 if(bAddSeparator)
896 popupMenu.AppendMenu(MF_SEPARATOR);
898 bAddSeparator = false;
899 if (m_bHasWC)
901 popupMenu.AppendMenuIcon(eCmd_Switch, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS)), IDI_SWITCH);
902 popupMenu.AppendMenu(MF_SEPARATOR);
905 if(bShowCreateBranchOption)
907 bAddSeparator = true;
908 temp.LoadString(IDS_MENUBRANCH);
909 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
912 if (bShowEditBranchDescriptionOption)
914 bAddSeparator = true;
915 popupMenu.AppendMenuIcon(eCmd_EditBranchDescription, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION)), IDI_RENAME);
917 if(bShowRenameOption)
919 bAddSeparator = true;
920 popupMenu.AppendMenuIcon(eCmd_Rename, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME)), IDI_RENAME);
923 else if(selectedLeafs.size() == 2)
925 bAddSeparator = true;
926 popupMenu.AppendMenuIcon(eCmd_Diff, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS)), IDI_DIFF);
927 CString menu;
928 menu.Format(IDS_SHOWLOG_OF, GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("..")));
929 popupMenu.AppendMenuIcon(eCmd_ViewLogRange, menu, IDI_LOG);
930 menu.Format(IDS_SHOWLOG_OF, GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("...")));
931 popupMenu.AppendMenuIcon(eCmd_ViewLogRangeReachableFromOnlyOne, menu, IDI_LOG);
934 if(!selectedLeafs.empty())
936 if(AreAllFrom(selectedLeafs, L"refs/remotes/"))
938 if(bAddSeparator)
939 popupMenu.AppendMenu(MF_SEPARATOR);
940 CString menuItemName;
941 if(selectedLeafs.size() == 1)
942 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH);
943 else
944 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES, selectedLeafs.size());
946 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteBranch, menuItemName, IDI_DELETE);
947 bAddSeparator = true;
949 else if(AreAllFrom(selectedLeafs, L"refs/heads/"))
951 if(bAddSeparator)
952 popupMenu.AppendMenu(MF_SEPARATOR);
953 CString menuItemName;
954 if(selectedLeafs.size() == 1)
955 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH);
956 else
957 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES, selectedLeafs.size());
959 popupMenu.AppendMenuIcon(eCmd_DeleteBranch, menuItemName, IDI_DELETE);
960 bAddSeparator = true;
962 else if(AreAllFrom(selectedLeafs, L"refs/tags/"))
964 if(bAddSeparator)
965 popupMenu.AppendMenu(MF_SEPARATOR);
966 CString menuItemName;
967 if(selectedLeafs.size() == 1)
968 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETETAG);
969 else
970 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETETAGS, selectedLeafs.size());
972 popupMenu.AppendMenuIcon(eCmd_DeleteTag, menuItemName, IDI_DELETE);
973 bAddSeparator = true;
978 if(hTreePos!=NULL && selectedLeafs.empty())
980 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);
981 if(pTree->IsFrom(L"refs/remotes"))
983 if(bAddSeparator)
984 popupMenu.AppendMenu(MF_SEPARATOR);
985 popupMenu.AppendMenuIcon(eCmd_ManageRemotes, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES)), IDI_SETTINGS);
986 bAddSeparator = true;
987 if(selectedLeafs.empty())
989 CString remoteBranch;
990 if (SplitRemoteBranchName(pTree->GetRefName(), remoteName, remoteBranch))
991 remoteName = _T("");
992 if(!remoteName.IsEmpty())
994 CString temp;
995 temp.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
996 popupMenu.AppendMenuIcon(eCmd_Fetch, temp, IDI_PULL);
998 temp.LoadString(IDS_DELETEREMOTETAG);
999 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteTag, temp, IDI_DELETE);
1003 if(pTree->IsFrom(L"refs/heads"))
1005 if(bAddSeparator)
1006 popupMenu.AppendMenu(MF_SEPARATOR);
1007 CString temp;
1008 temp.LoadString(IDS_MENUBRANCH);
1009 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
1011 if(pTree->IsFrom(L"refs/tags"))
1013 if(bAddSeparator)
1014 popupMenu.AppendMenu(MF_SEPARATOR);
1015 CString temp;
1016 temp.LoadString(IDS_MENUTAG);
1017 popupMenu.AppendMenuIcon(eCmd_CreateTag, temp, IDI_TAG);
1018 temp.LoadString(IDS_PROC_BROWSEREFS_DELETEALLTAGS);
1019 popupMenu.AppendMenuIcon(eCmd_DeleteAllTags, temp, IDI_DELETE);
1024 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
1025 switch(cmd)
1027 case eCmd_ViewLog:
1029 CLogDlg dlg;
1030 dlg.SetRange(g_Git.FixBranchName(selectedLeafs[0]->GetRefName()));
1031 dlg.DoModal();
1033 break;
1034 case eCmd_ViewLogRange:
1036 CLogDlg dlg;
1037 dlg.SetRange(GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("..")));
1038 dlg.DoModal();
1040 break;
1041 case eCmd_ViewLogRangeReachableFromOnlyOne:
1043 CLogDlg dlg;
1044 dlg.SetRange(GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("...")));
1045 dlg.DoModal();
1047 break;
1048 case eCmd_RepoBrowser:
1049 CAppUtils::RunTortoiseGitProc(_T("/command:repobrowser /path:\"") + g_Git.m_CurrentDir + _T("\" /rev:") + selectedLeafs[0]->GetRefName());
1050 break;
1051 case eCmd_DeleteBranch:
1052 case eCmd_DeleteRemoteBranch:
1054 if(ConfirmDeleteRef(selectedLeafs))
1055 DoDeleteRefs(selectedLeafs, true);
1056 Refresh();
1058 break;
1059 case eCmd_DeleteTag:
1061 if(ConfirmDeleteRef(selectedLeafs))
1062 DoDeleteRefs(selectedLeafs, true);
1063 Refresh();
1065 break;
1066 case eCmd_ShowReflog:
1068 CRefLogDlg refLogDlg(this);
1069 refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();
1070 refLogDlg.DoModal();
1072 break;
1073 case eCmd_Fetch:
1075 CAppUtils::Fetch(remoteName);
1076 Refresh();
1078 break;
1079 case eCmd_DeleteRemoteTag:
1081 CDeleteRemoteTagDlg deleteRemoteTagDlg;
1082 deleteRemoteTagDlg.m_sRemote = remoteName;
1083 deleteRemoteTagDlg.DoModal();
1085 break;
1086 case eCmd_Switch:
1088 CAppUtils::Switch(selectedLeafs[0]->GetRefName());
1090 break;
1091 case eCmd_Rename:
1093 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1094 if(pos != NULL)
1095 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1097 break;
1098 case eCmd_AddRemote:
1100 CAddRemoteDlg(this).DoModal();
1101 Refresh();
1103 break;
1104 case eCmd_ManageRemotes:
1106 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS)), new CSettingGitRemote(g_Git.m_CurrentDir), this).DoModal();
1107 // CSettingGitRemote W_Remotes(m_cmdPath);
1108 // W_Remotes.DoModal();
1109 Refresh();
1111 break;
1112 case eCmd_CreateBranch:
1114 CString *commitHash = NULL;
1115 if (selectedLeafs.size() == 1)
1116 commitHash = &(selectedLeafs[0]->m_csRefHash);
1117 CAppUtils::CreateBranchTag(false, commitHash);
1118 Refresh();
1120 break;
1121 case eCmd_CreateTag:
1123 CAppUtils::CreateBranchTag(true);
1124 Refresh();
1126 break;
1127 case eCmd_DeleteAllTags:
1129 for (int i = 0; i < m_ListRefLeafs.GetItemCount(); ++i)
1131 m_ListRefLeafs.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
1132 selectedLeafs.push_back((CShadowTree*)m_ListRefLeafs.GetItemData(i));
1134 if (ConfirmDeleteRef(selectedLeafs))
1135 DoDeleteRefs(selectedLeafs, true);
1136 Refresh();
1138 break;
1139 case eCmd_Diff:
1141 CFileDiffDlg dlg;
1142 dlg.SetDiff(
1143 NULL,
1144 selectedLeafs[0]->m_csRefHash,
1145 selectedLeafs[1]->m_csRefHash);
1146 dlg.DoModal();
1148 break;
1149 case eCmd_EditBranchDescription:
1151 CInputDlg dlg;
1152 dlg.m_sHintText = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1153 dlg.m_sInputText = selectedLeafs[0]->m_csDescription;
1154 dlg.m_sTitle = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1155 dlg.m_bUseLogWidth = true;
1156 if(dlg.DoModal() == IDOK)
1158 CString key;
1159 key.Format(_T("branch.%s.description"), selectedLeafs[0]->m_csRefName);
1160 dlg.m_sInputText.Replace(_T("\r"), _T(""));
1161 dlg.m_sInputText.Trim();
1162 if (dlg.m_sInputText.IsEmpty())
1163 g_Git.UnsetConfigValue(key);
1164 else
1165 g_Git.SetConfigValue(key, dlg.m_sInputText);
1166 Refresh();
1169 break;
1173 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree& leafs, const wchar_t* from)
1175 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
1176 if(!(*i)->IsFrom(from))
1177 return false;
1178 return true;
1181 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
1183 if (pMsg->message == WM_KEYDOWN)
1185 switch (pMsg->wParam)
1187 /* case VK_RETURN:
1189 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1191 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1193 PostMessage(WM_COMMAND, IDOK);
1195 return TRUE;
1198 break;
1199 */ case VK_F2:
1201 if(pMsg->hwnd == m_ListRefLeafs.m_hWnd)
1203 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1204 if(pos != NULL)
1205 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1208 break;
1210 case VK_F5:
1212 Refresh();
1214 break;
1219 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
1222 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1224 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1225 *pResult = 0;
1227 if(m_currSortCol == pNMLV->iSubItem)
1228 m_currSortDesc = !m_currSortDesc;
1229 else
1231 m_currSortCol = pNMLV->iSubItem;
1232 m_currSortDesc = false;
1235 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
1236 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
1238 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
1241 void CBrowseRefsDlg::OnDestroy()
1243 m_pickedRef = GetSelectedRef(true, false);
1245 CResizableStandAloneDialog::OnDestroy();
1248 void CBrowseRefsDlg::OnItemChangedListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1250 LPNMLISTVIEW pNMListView = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1251 *pResult = 0;
1253 CShadowTree *item = (CShadowTree*)m_ListRefLeafs.GetItemData(pNMListView->iItem);
1254 if (item && pNMListView->uNewState == 2)
1255 m_sLastSelected = item->GetRefName();
1258 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR * /*pNMHDR*/, LRESULT *pResult)
1260 *pResult = 0;
1262 EndDialog(IDOK);
1265 CString CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef, int pickRef_Kind, bool pickMultipleRefs)
1267 CBrowseRefsDlg dlg(CString(),NULL);
1269 if(initialRef.IsEmpty())
1270 initialRef = L"HEAD";
1271 dlg.m_initialRef = initialRef;
1272 dlg.m_pickRef_Kind = pickRef_Kind;
1273 dlg.m_bPickOne = !pickMultipleRefs;
1275 if(dlg.DoModal() != IDOK)
1276 return CString();
1278 return dlg.m_pickedRef;
1281 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)
1283 CString origRef;
1284 pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);
1285 CString resultRef = PickRef(false,origRef,pickRef_Kind);
1286 if(resultRef.IsEmpty())
1287 return false;
1288 if(wcsncmp(resultRef,L"refs/",5)==0)
1289 resultRef = resultRef.Mid(5);
1290 // if(wcsncmp(resultRef,L"heads/",6)==0)
1291 // resultRef = resultRef.Mid(6);
1293 //Find closest match of choice in combobox
1294 int ixFound = -1;
1295 int matchLength = 0;
1296 CString comboRefName;
1297 for(int i = 0; i < pComboBox->GetCount(); ++i)
1299 pComboBox->GetLBText(i, comboRefName);
1300 if(comboRefName.Find(L'/') < 0 && !comboRefName.IsEmpty())
1301 comboRefName.Insert(0,L"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1302 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)
1304 matchLength = comboRefName.GetLength();
1305 ixFound = i;
1308 if(ixFound >= 0)
1309 pComboBox->SetCurSel(ixFound);
1310 else
1311 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)
1313 return true;
1316 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1318 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1319 *pResult = FALSE;
1321 if(pDispInfo->item.pszText == NULL)
1322 return; //User canceled changing
1324 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1326 if(!pTree->IsFrom(L"refs/heads"))
1328 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES, IDS_APPNAME, MB_OK | MB_ICONERROR);
1329 return;
1332 CString origName = pTree->GetRefName().Mid(11);
1334 CString newName;
1335 if(m_pListCtrlRoot != NULL)
1336 newName = m_pListCtrlRoot->GetRefName() + L'/';
1337 newName += pDispInfo->item.pszText;
1339 if(wcsncmp(newName,L"refs/heads/",11)!=0)
1341 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE, IDS_APPNAME, MB_OK | MB_ICONERROR);
1342 return;
1345 CString newNameTrunced = newName.Mid(11);
1347 CString errorMsg;
1348 if(g_Git.Run(L"git branch -m \"" + origName + L"\" \"" + newNameTrunced + L"\"", &errorMsg, CP_UTF8) != 0)
1350 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1351 return;
1353 //Do as if it failed to rename. Let Refresh() do the job.
1354 //*pResult = TRUE;
1356 Refresh(newName);
1358 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1360 // AfxMessageBox(W_csPopup);
1364 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1366 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1367 *pResult = FALSE;
1369 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1371 if(!pTree->IsFrom(L"refs/heads"))
1373 *pResult = TRUE; //Dont allow renaming any other things then branches at the moment.
1374 return;
1378 void CBrowseRefsDlg::OnEnChangeEditFilter()
1380 SetTimer(IDT_FILTER, 1000, NULL);
1383 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent)
1385 if (nIDEvent == IDT_FILTER)
1387 KillTimer(IDT_FILTER);
1388 FillListCtrlForTreeNode(m_RefTreeCtrl.GetSelectedItem());
1391 CResizableStandAloneDialog::OnTimer(nIDEvent);
1394 LRESULT CBrowseRefsDlg::OnClickedInfoIcon(WPARAM /*wParam*/, LPARAM lParam)
1396 // FIXME: x64 version would get this function called with unexpected parameters.
1397 if (!lParam)
1398 return 0;
1400 RECT * rect = (LPRECT)lParam;
1401 CPoint point;
1402 CString temp;
1403 point = CPoint(rect->left, rect->bottom);
1404 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1405 CMenu popup;
1406 if (popup.CreatePopupMenu())
1408 temp.LoadString(IDS_LOG_FILTER_REFNAME);
1409 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME), LOGFILTER_REFNAME, temp);
1411 temp.LoadString(IDS_LOG_FILTER_SUBJECT);
1412 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT), LOGFILTER_SUBJECT, temp);
1414 temp.LoadString(IDS_LOG_FILTER_AUTHORS);
1415 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS), LOGFILTER_AUTHORS, temp);
1417 temp.LoadString(IDS_LOG_FILTER_REVS);
1418 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS), LOGFILTER_REVS, temp);
1420 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1421 if (selection != 0)
1423 m_SelectedFilters ^= selection;
1424 SetFilterCueText();
1425 SetTimer(IDT_FILTER, 1000, NULL);
1428 return 0L;
1431 void CBrowseRefsDlg::SetFilterCueText()
1433 CString temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY));
1434 temp += _T(" ");
1436 if (m_SelectedFilters & LOGFILTER_REFNAME)
1437 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME));
1439 if (m_SelectedFilters & LOGFILTER_SUBJECT)
1441 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1442 temp += _T(", ");
1443 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT));
1446 if (m_SelectedFilters & LOGFILTER_AUTHORS)
1448 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1449 temp += _T(", ");
1450 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS));
1453 if (m_SelectedFilters & LOGFILTER_REVS)
1455 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1456 temp += _T(", ");
1457 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS));
1460 // to make the cue banner text appear more to the right of the edit control
1461 temp = _T(" ") + temp;
1462 m_ctrlFilter.SetCueBanner(temp.TrimRight());