Randomize Sync Dialog startup position
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blob391b6eaa5975122371d12da0cda87f18e5804eef
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_ColumnManager(&m_ListRefLeafs),
162 m_bPickOne(false)
167 CBrowseRefsDlg::~CBrowseRefsDlg()
171 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)
173 CDialog::DoDataExchange(pDX);
174 DDX_Control(pDX, IDC_TREE_REF, m_RefTreeCtrl);
175 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_ListRefLeafs);
176 DDX_Control(pDX, IDC_BROWSEREFS_EDIT_FILTER, m_ctrlFilter);
180 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)
181 ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)
182 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)
183 ON_WM_CONTEXTMENU()
184 ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)
185 ON_WM_DESTROY()
186 ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)
187 ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnItemChangedListRefLeafs)
188 ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs)
189 ON_NOTIFY(LVN_BEGINLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs)
190 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER, &CBrowseRefsDlg::OnEnChangeEditFilter)
191 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED, OnClickedInfoIcon)
192 ON_WM_TIMER()
193 END_MESSAGE_MAP()
196 // CBrowseRefsDlg message handlers
198 void CBrowseRefsDlg::OnBnClickedOk()
200 OnOK();
203 BOOL CBrowseRefsDlg::OnInitDialog()
205 CResizableStandAloneDialog::OnInitDialog();
206 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
208 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
209 m_ctrlFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
210 m_ctrlFilter.SetInfoIcon(IDI_FILTEREDIT);
211 SetFilterCueText();
213 AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);
214 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
215 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER, TOP_LEFT);
216 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER, TOP_LEFT, TOP_RIGHT);
217 AddAnchor(IDHELP, BOTTOM_RIGHT);
219 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
220 static UINT columnNames[] = { IDS_BRANCHNAME, IDS_DATELASTCOMMIT, IDS_LASTCOMMIT, IDS_LASTAUTHOR, IDS_HASH, IDS_DESCRIPTION };
221 static int columnWidths[] = { 150, 100, 300, 100, 80, 80 };
222 DWORD dwDefaultColumns = (1 << eCol_Name) | (1 << eCol_Date) | (1 << eCol_Msg) |
223 (1 << eCol_LastAuthor) | (1 << eCol_Hash) | (1 << eCol_Description);
224 m_ColumnManager.SetNames(columnNames, _countof(columnNames));
225 m_ColumnManager.ReadSettings(dwDefaultColumns, 0, _T("BrowseRefs"), _countof(columnNames), columnWidths);
227 AddAnchor(IDOK,BOTTOM_RIGHT);
228 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
230 Refresh(m_initialRef);
232 EnableSaveRestore(L"BrowseRefs");
234 CString sWindowTitle;
235 GetWindowText(sWindowTitle);
236 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
238 m_bHasWC = !g_GitAdminDir.IsBareRepo(g_Git.m_CurrentDir);
240 if (m_bPickOne)
241 m_ListRefLeafs.ModifyStyle(0, LVS_SINGLESEL);
243 m_ListRefLeafs.SetFocus();
244 return FALSE;
247 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
249 int posSlash=nameLeft.Find('/');
250 CString nameSub;
251 if(posSlash<0)
253 nameSub=nameLeft;
254 nameLeft.Empty();//Nothing left
256 else
258 nameSub=nameLeft.Left(posSlash);
259 nameLeft=nameLeft.Mid(posSlash+1);
261 if(nameSub.IsEmpty())
262 return NULL;
264 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
265 return NULL;
267 CShadowTree& nextNode=m_ShadowTree[nameSub];
268 nextNode.m_csRefName=nameSub;
269 nextNode.m_pParent=this;
270 return &nextNode;
273 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)
275 if(IsLeaf())
277 if(m_csRefName.GetLength() > partialRefName.GetLength())
278 return NULL;
279 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)
281 //Match of leaf name. Try match on total name.
282 CString totalRefName = GetRefName();
283 if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)
284 return this; //Also match. Found.
287 else
289 //Not a leaf. Search all nodes.
290 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)
292 CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);
293 if(pSubtree != NULL)
294 return pSubtree; //Found
297 return NULL;//Not found
301 typedef std::map<CString,CString> MAP_STRING_STRING;
303 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf, bool pickFirstSelIfMultiSel)
305 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
306 //List ctrl selection?
307 if(pos && (pickFirstSelIfMultiSel || m_ListRefLeafs.GetSelectedCount() == 1))
309 //A leaf is selected
310 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(
311 m_ListRefLeafs.GetNextSelectedItem(pos));
312 return pTree->GetRefName();
314 else if (pos && !pickFirstSelIfMultiSel)
316 // at least one leaf is selected
317 CString refs;
318 int index;
319 while ((index = m_ListRefLeafs.GetNextSelectedItem(pos)) >= 0)
321 CString ref = ((CShadowTree*)m_ListRefLeafs.GetItemData(index))->GetRefName();
322 if(wcsncmp(ref, L"refs/", 5) == 0)
323 ref = ref.Mid(5);
324 if(wcsncmp(ref, L"heads/", 6) == 0)
325 ref = ref.Mid(6);
326 refs += ref + _T(" ");
328 return refs.Trim();
330 else if(!onlyIfLeaf)
332 //Tree ctrl selection?
333 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
334 if(hTree!=NULL)
336 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);
337 return pTree->GetRefName();
340 return CString();//None
343 static int GetBranchDescriptionsCallback(const git_config_entry *entry, void *data)
345 MAP_STRING_STRING *descriptions = (MAP_STRING_STRING *) data;
346 CString key = CUnicodeUtils::GetUnicode(entry->name, CP_UTF8);
347 CString val = CUnicodeUtils::GetUnicode(entry->value, CP_UTF8);
348 descriptions->insert(std::make_pair(key.Mid(7, key.GetLength() - 7 - 12), val)); // 7: branch., 12: .description
349 return 0;
352 MAP_STRING_STRING GetBranchDescriptions()
354 MAP_STRING_STRING descriptions;
355 git_config * config;
356 git_config_new(&config);
357 CStringA projectConfigA = CUnicodeUtils::GetMulti(g_Git.GetGitLocalConfig(), CP_UTF8);
358 git_config_add_file_ondisk(config, projectConfigA.GetBuffer(), GIT_CONFIG_LEVEL_LOCAL, FALSE);
359 projectConfigA.ReleaseBuffer();
360 git_config_foreach_match(config, "branch\\..*\\.description", GetBranchDescriptionsCallback, &descriptions);
361 git_config_free(config);
362 return descriptions;
365 void CBrowseRefsDlg::Refresh(CString selectRef)
367 // m_RefMap.clear();
368 // g_Git.GetMapHashToFriendName(m_RefMap);
370 if(!selectRef.IsEmpty())
372 if(selectRef == "HEAD")
374 selectRef = g_Git.GetSymbolicRef(selectRef, false);
377 else
379 selectRef = GetSelectedRef(false, true);
382 m_RefTreeCtrl.DeleteAllItems();
383 m_ListRefLeafs.DeleteAllItems();
384 m_TreeRoot.m_ShadowTree.clear();
385 m_TreeRoot.m_csRefName = "refs";
386 m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);
387 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);
389 CString allRefs, error;
390 if (g_Git.Run(L"git for-each-ref --format="
391 L"%(refname)%04"
392 L"%(objectname)%04"
393 L"%(authordate:relative)%04"
394 L"%(subject)%04"
395 L"%(authorname)%04"
396 L"%(authordate:iso8601)%03",
397 &allRefs, &error, CP_UTF8))
399 CMessageBox::Show(NULL, CString(_T("Get refs failed\n")) + error, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
402 int linePos=0;
403 CString singleRef;
405 MAP_STRING_STRING refMap;
407 //First sort on ref name
408 while(!(singleRef=allRefs.Tokenize(L"\03",linePos)).IsEmpty())
410 singleRef.TrimLeft(L"\r\n");
411 int valuePos=0;
412 CString refName=singleRef.Tokenize(L"\04",valuePos);
413 if(refName.IsEmpty())
414 continue;
415 CString refRest=singleRef.Mid(valuePos);
418 //Use ref based on m_pickRef_Kind
419 if(wcsncmp(refName,L"refs/heads",10)==0 && !(m_pickRef_Kind & gPickRef_Head) )
420 continue; //Skip
421 if(wcsncmp(refName,L"refs/tags",9)==0 && !(m_pickRef_Kind & gPickRef_Tag) )
422 continue; //Skip
423 if(wcsncmp(refName,L"refs/remotes",12)==0 && !(m_pickRef_Kind & gPickRef_Remote) )
424 continue; //Skip
426 refMap[refName] = refRest; //Use
429 MAP_STRING_STRING descriptions = GetBranchDescriptions();
431 //Populate ref tree
432 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
434 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
435 CString values=iterRefMap->second;
436 values.Replace(L"\04" L"\04",L"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
438 int valuePos=0;
439 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
440 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
441 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
442 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
443 treeLeaf.m_csDate_Iso8601= values.Tokenize(L"\04",valuePos);
445 if (wcsncmp(iterRefMap->first, L"refs/heads", 10) == 0)
446 treeLeaf.m_csDescription = descriptions[treeLeaf.m_csRefName];
449 // try exact match first
450 if(selectRef.IsEmpty() || !(SelectRef(selectRef, true) || SelectRef(selectRef, false)))
451 //Probably not on a branch. Select root node.
452 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);
456 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
458 if(!bExactMatch)
460 CString newRefName = GetFullRefName(refName);
461 if(!newRefName.IsEmpty())
462 refName = newRefName;
463 //else refName is not a valid ref. Try to select as good as possible.
465 if(_wcsnicmp(refName, L"refs/", 5) != 0)
466 return false; // Not a ref name
468 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
469 if(treeLeafHead.m_hTree != NULL)
471 //Not a leaf. Select tree node and return
472 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
473 return true;
476 if(treeLeafHead.m_pParent==NULL)
477 return false; //Weird... should not occur.
479 //This is the current head.
480 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
482 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
484 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
485 if(pCurrShadowTree == &treeLeafHead)
487 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
488 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
492 return true;
495 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
497 if(pTreePos==NULL)
499 if(_wcsnicmp(refName, L"refs/", 5) == 0)
500 refName=refName.Mid(5);
501 pTreePos=&m_TreeRoot;
503 if(refName.IsEmpty())
504 return *pTreePos;//Found leaf
506 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
507 if(pNextTree==NULL)
509 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
510 ASSERT(!bCreateIfNotExist);
511 return *pTreePos;
514 if(!refName.IsEmpty())
516 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
517 //Leafs are for the list control.
518 if(pNextTree->m_hTree==NULL)
520 //New tree. Create node in control.
521 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
522 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
526 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
530 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
532 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
533 *pResult = 0;
535 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
538 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
540 m_ListRefLeafs.DeleteAllItems();
542 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
543 if(pTree==NULL)
545 ASSERT(FALSE);
546 return;
548 FillListCtrlForShadowTree(pTree,L"",true);
551 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
553 if(pTree->IsLeaf())
555 CString filter;
556 m_ctrlFilter.GetWindowText(filter);
557 filter.MakeLower();
558 CString ref = refNamePrefix + pTree->m_csRefName;
559 if (!(pTree->m_csRefName.IsEmpty() || pTree->m_csRefName == "refs" && pTree->m_pParent == NULL) && IsMatchFilter(pTree, ref, filter))
561 int indexItem = m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(), L"");
563 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
564 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, ref);
565 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);
566 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
567 m_ListRefLeafs.SetItemText(indexItem,eCol_LastAuthor, pTree->m_csAuthor);
568 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
569 int pos = 0;
570 m_ListRefLeafs.SetItemText(indexItem,eCol_Description, pTree->m_csDescription.Tokenize(_T("\n"), pos));
573 else
576 CString csThisName;
577 if(!isFirstLevel)
578 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
579 else
580 m_pListCtrlRoot = pTree;
581 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
583 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
586 if (isFirstLevel)
588 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
589 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
591 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
595 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree* pTree, const CString &ref, const CString &filter)
597 if (m_SelectedFilters & LOGFILTER_REFNAME)
599 CString msg = ref;
600 msg = msg.MakeLower();
602 if (msg.Find(filter) >= 0)
603 return true;
606 if (m_SelectedFilters & LOGFILTER_SUBJECT)
608 CString msg = pTree->m_csSubject;
609 msg = msg.MakeLower();
611 if (msg.Find(filter) >= 0)
612 return true;
615 if (m_SelectedFilters & LOGFILTER_AUTHORS)
617 CString msg = pTree->m_csAuthor;
618 msg = msg.MakeLower();
620 if (msg.Find(filter) >= 0)
621 return true;
624 if (m_SelectedFilters & LOGFILTER_REVS)
626 CString msg = pTree->m_csRefHash;
627 msg = msg.MakeLower();
629 if (msg.Find(filter) >= 0)
630 return true;
632 return false;
635 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree& leafs)
637 ASSERT(!leafs.empty());
639 CString csMessage;
640 UINT mbIcon=MB_ICONQUESTION;
642 bool bIsRemoteBranch = false;
643 bool bIsBranch = false;
644 if (leafs[0]->IsFrom(L"refs/remotes")) {bIsBranch = true; bIsRemoteBranch = true;}
645 else if (leafs[0]->IsFrom(L"refs/heads")) {bIsBranch = true;}
647 if(bIsBranch)
649 if(leafs.size() == 1)
651 CString branchToDelete = leafs[0]->GetRefName().Mid(bIsRemoteBranch ? 13 : 11);
652 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, branchToDelete);
654 //Check if branch is fully merged in HEAD
655 if (!g_Git.IsFastForward(leafs[0]->GetRefName(), _T("HEAD")))
657 csMessage += L"\r\n\r\n";
658 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED));
659 mbIcon = MB_ICONWARNING;
662 if(bIsRemoteBranch)
664 csMessage += L"\r\n\r\n";
665 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
666 mbIcon = MB_ICONWARNING;
669 else
671 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
673 csMessage += L"\r\n\r\n";
674 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK));
675 mbIcon = MB_ICONWARNING;
677 if(bIsRemoteBranch)
679 csMessage += L"\r\n\r\n";
680 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
681 mbIcon = MB_ICONWARNING;
686 else if(leafs[0]->IsFrom(L"refs/tags"))
688 if(leafs.size() == 1)
690 CString tagToDelete = leafs[0]->GetRefName().Mid(10);
691 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, tagToDelete);
693 else
695 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
699 return CMessageBox::Show(m_hWnd, csMessage, _T("TortoiseGit"), MB_YESNO | mbIcon) == IDYES;
703 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree& leafs, bool bForce)
705 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
706 if(!DoDeleteRef((*i)->GetRefName(), bForce))
707 return false;
708 return true;
711 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)
713 bool bIsRemoteBranch = false;
714 bool bIsBranch = false;
715 if (wcsncmp(completeRefName, L"refs/remotes",12)==0) {bIsBranch = true; bIsRemoteBranch = true;}
716 else if (wcsncmp(completeRefName, L"refs/heads",10)==0) {bIsBranch = true;}
718 if(bIsBranch)
720 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);
721 CString cmd;
722 if(bIsRemoteBranch)
724 CString remoteName, remoteBranchToDelete;
725 if (SplitRemoteBranchName(branchToDelete, remoteName, remoteBranchToDelete))
726 return false;
728 if(CAppUtils::IsSSHPutty())
730 CAppUtils::LaunchPAgent(NULL, &remoteName);
733 cmd.Format(L"git.exe push \"%s\" :refs/heads/%s", remoteName, remoteBranchToDelete);
735 else
736 cmd.Format(L"git.exe branch -%c -- %s",bForce?L'D':L'd',branchToDelete);
737 CSysProgressDlg sysProgressDlg;
738 if (bIsRemoteBranch)
740 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
741 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS)));
742 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
743 sysProgressDlg.SetShowProgressBar(false);
744 sysProgressDlg.ShowModal(this, true);
746 CString errorMsg;
747 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
749 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
750 if (bIsRemoteBranch)
751 sysProgressDlg.Stop();
752 BringWindowToTop();
753 return false;
755 if (bIsRemoteBranch)
756 sysProgressDlg.Stop();
757 BringWindowToTop();
759 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
761 CString tagToDelete = completeRefName.Mid(10);
762 CString cmd;
763 cmd.Format(L"git.exe tag -d -- %s",tagToDelete);
764 CString errorMsg;
765 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
767 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
768 return false;
771 return true;
774 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
776 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
777 if(pLeaf == NULL)
778 return CString();
779 return pLeaf->GetRefName();
783 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
785 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
786 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
789 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
791 CPoint clientPoint=point;
792 m_RefTreeCtrl.ScreenToClient(&clientPoint);
794 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
795 if(hTreeItem!=NULL)
796 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
798 VectorPShadowTree tree;
799 ShowContextMenu(point,hTreeItem,tree);
803 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
805 std::vector<CShadowTree*> selectedLeafs;
806 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
807 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
808 while(pos)
810 selectedLeafs.push_back(
811 (CShadowTree*)m_ListRefLeafs.GetItemData(
812 m_ListRefLeafs.GetNextSelectedItem(pos)));
815 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
818 CString GetTwoSelectedRefs(VectorPShadowTree& selectedLeafs, const CString &lastSelected, const CString &separator)
820 ASSERT(selectedLeafs.size() == 2);
822 if (selectedLeafs.at(0)->GetRefName() == lastSelected)
823 return g_Git.StripRefName(selectedLeafs.at(1)->GetRefName()) + separator + g_Git.StripRefName(lastSelected);
824 else
825 return g_Git.StripRefName(selectedLeafs.at(0)->GetRefName()) + separator + g_Git.StripRefName(lastSelected);
828 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
830 CIconMenu popupMenu;
831 popupMenu.CreatePopupMenu();
833 bool bAddSeparator = false;
834 CString remoteName;
836 if(selectedLeafs.size()==1)
838 bAddSeparator = true;
840 bool bShowReflogOption = false;
841 bool bShowFetchOption = false;
842 bool bShowSwitchOption = false;
843 bool bShowRenameOption = false;
844 bool bShowCreateBranchOption = false;
845 bool bShowEditBranchDescriptionOption = false;
847 CString fetchFromCmd;
849 if(selectedLeafs[0]->IsFrom(L"refs/heads"))
851 bShowReflogOption = true;
852 bShowSwitchOption = true;
853 bShowRenameOption = true;
854 bShowEditBranchDescriptionOption = true;
856 else if(selectedLeafs[0]->IsFrom(L"refs/remotes"))
858 bShowReflogOption = true;
859 bShowFetchOption = true;
860 bShowCreateBranchOption = true;
862 CString remoteBranch;
863 if (SplitRemoteBranchName(selectedLeafs[0]->GetRefName(), remoteName, remoteBranch))
864 bShowFetchOption = false;
865 else
866 fetchFromCmd.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
868 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))
872 CString temp;
873 temp.LoadString(IDS_MENULOG);
874 popupMenu.AppendMenuIcon(eCmd_ViewLog, temp, IDI_LOG);
875 popupMenu.AppendMenuIcon(eCmd_RepoBrowser, IDS_LOG_BROWSEREPO, IDI_REPOBROWSE);
876 if(bShowReflogOption)
878 temp.LoadString(IDS_MENUREFLOG);
879 popupMenu.AppendMenuIcon(eCmd_ShowReflog, temp, IDI_LOG);
882 popupMenu.AppendMenu(MF_SEPARATOR);
883 bAddSeparator = false;
885 if(bShowFetchOption)
887 bAddSeparator = true;
888 popupMenu.AppendMenuIcon(eCmd_Fetch, fetchFromCmd, IDI_PULL);
891 if(bAddSeparator)
892 popupMenu.AppendMenu(MF_SEPARATOR);
894 bAddSeparator = false;
895 if (m_bHasWC)
897 CString format, str;
898 if (selectedLeafs[0]->GetRefName() != _T("refs/heads/") + g_Git.GetCurrentBranch())
900 format.LoadString(IDS_LOG_POPUP_MERGEREV);
901 str.Format(format, g_Git.GetCurrentBranch());
902 popupMenu.AppendMenuIcon(eCmd_Merge, str, IDI_MERGE);
904 popupMenu.AppendMenuIcon(eCmd_Switch, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS)), IDI_SWITCH);
905 popupMenu.AppendMenu(MF_SEPARATOR);
908 if(bShowCreateBranchOption)
910 bAddSeparator = true;
911 temp.LoadString(IDS_MENUBRANCH);
912 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
915 if (bShowEditBranchDescriptionOption)
917 bAddSeparator = true;
918 popupMenu.AppendMenuIcon(eCmd_EditBranchDescription, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION)), IDI_RENAME);
920 if(bShowRenameOption)
922 bAddSeparator = true;
923 popupMenu.AppendMenuIcon(eCmd_Rename, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME)), IDI_RENAME);
926 else if(selectedLeafs.size() == 2)
928 bAddSeparator = true;
929 popupMenu.AppendMenuIcon(eCmd_Diff, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS)), IDI_DIFF);
930 CString menu;
931 menu.Format(IDS_SHOWLOG_OF, GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("..")));
932 popupMenu.AppendMenuIcon(eCmd_ViewLogRange, menu, IDI_LOG);
933 menu.Format(IDS_SHOWLOG_OF, GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("...")));
934 popupMenu.AppendMenuIcon(eCmd_ViewLogRangeReachableFromOnlyOne, menu, IDI_LOG);
937 if(!selectedLeafs.empty())
939 if(AreAllFrom(selectedLeafs, L"refs/remotes/"))
941 if(bAddSeparator)
942 popupMenu.AppendMenu(MF_SEPARATOR);
943 CString menuItemName;
944 if(selectedLeafs.size() == 1)
945 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH);
946 else
947 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES, selectedLeafs.size());
949 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteBranch, menuItemName, IDI_DELETE);
950 bAddSeparator = true;
952 else if(AreAllFrom(selectedLeafs, L"refs/heads/"))
954 if(bAddSeparator)
955 popupMenu.AppendMenu(MF_SEPARATOR);
956 CString menuItemName;
957 if(selectedLeafs.size() == 1)
958 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH);
959 else
960 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES, selectedLeafs.size());
962 popupMenu.AppendMenuIcon(eCmd_DeleteBranch, menuItemName, IDI_DELETE);
963 bAddSeparator = true;
965 else if(AreAllFrom(selectedLeafs, L"refs/tags/"))
967 if(bAddSeparator)
968 popupMenu.AppendMenu(MF_SEPARATOR);
969 CString menuItemName;
970 if(selectedLeafs.size() == 1)
971 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETETAG);
972 else
973 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETETAGS, selectedLeafs.size());
975 popupMenu.AppendMenuIcon(eCmd_DeleteTag, menuItemName, IDI_DELETE);
976 bAddSeparator = true;
981 if(hTreePos!=NULL && selectedLeafs.empty())
983 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);
984 if(pTree->IsFrom(L"refs/remotes"))
986 if(bAddSeparator)
987 popupMenu.AppendMenu(MF_SEPARATOR);
988 popupMenu.AppendMenuIcon(eCmd_ManageRemotes, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES)), IDI_SETTINGS);
989 bAddSeparator = true;
990 if(selectedLeafs.empty())
992 CString remoteBranch;
993 if (SplitRemoteBranchName(pTree->GetRefName(), remoteName, remoteBranch))
994 remoteName = _T("");
995 if(!remoteName.IsEmpty())
997 CString temp;
998 temp.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
999 popupMenu.AppendMenuIcon(eCmd_Fetch, temp, IDI_PULL);
1001 temp.LoadString(IDS_DELETEREMOTETAG);
1002 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteTag, temp, IDI_DELETE);
1006 if(pTree->IsFrom(L"refs/heads"))
1008 if(bAddSeparator)
1009 popupMenu.AppendMenu(MF_SEPARATOR);
1010 CString temp;
1011 temp.LoadString(IDS_MENUBRANCH);
1012 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
1014 if(pTree->IsFrom(L"refs/tags"))
1016 if(bAddSeparator)
1017 popupMenu.AppendMenu(MF_SEPARATOR);
1018 CString temp;
1019 temp.LoadString(IDS_MENUTAG);
1020 popupMenu.AppendMenuIcon(eCmd_CreateTag, temp, IDI_TAG);
1021 temp.LoadString(IDS_PROC_BROWSEREFS_DELETEALLTAGS);
1022 popupMenu.AppendMenuIcon(eCmd_DeleteAllTags, temp, IDI_DELETE);
1027 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
1028 switch(cmd)
1030 case eCmd_ViewLog:
1032 CLogDlg dlg;
1033 dlg.SetRange(g_Git.FixBranchName(selectedLeafs[0]->GetRefName()));
1034 dlg.DoModal();
1036 break;
1037 case eCmd_ViewLogRange:
1039 CLogDlg dlg;
1040 dlg.SetRange(GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("..")));
1041 dlg.DoModal();
1043 break;
1044 case eCmd_ViewLogRangeReachableFromOnlyOne:
1046 CLogDlg dlg;
1047 dlg.SetRange(GetTwoSelectedRefs(selectedLeafs, m_sLastSelected, _T("...")));
1048 dlg.DoModal();
1050 break;
1051 case eCmd_RepoBrowser:
1052 CAppUtils::RunTortoiseGitProc(_T("/command:repobrowser /path:\"") + g_Git.m_CurrentDir + _T("\" /rev:") + selectedLeafs[0]->GetRefName());
1053 break;
1054 case eCmd_DeleteBranch:
1055 case eCmd_DeleteRemoteBranch:
1057 if(ConfirmDeleteRef(selectedLeafs))
1058 DoDeleteRefs(selectedLeafs, true);
1059 Refresh();
1061 break;
1062 case eCmd_DeleteTag:
1064 if(ConfirmDeleteRef(selectedLeafs))
1065 DoDeleteRefs(selectedLeafs, true);
1066 Refresh();
1068 break;
1069 case eCmd_ShowReflog:
1071 CRefLogDlg refLogDlg(this);
1072 refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();
1073 refLogDlg.DoModal();
1075 break;
1076 case eCmd_Fetch:
1078 CAppUtils::Fetch(remoteName);
1079 Refresh();
1081 break;
1082 case eCmd_DeleteRemoteTag:
1084 CDeleteRemoteTagDlg deleteRemoteTagDlg;
1085 deleteRemoteTagDlg.m_sRemote = remoteName;
1086 deleteRemoteTagDlg.DoModal();
1088 break;
1089 case eCmd_Merge:
1091 CString ref = selectedLeafs[0]->GetRefName();
1092 CAppUtils::Merge(&ref);
1094 break;
1095 case eCmd_Switch:
1097 CAppUtils::Switch(selectedLeafs[0]->GetRefName());
1099 break;
1100 case eCmd_Rename:
1102 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1103 if(pos != NULL)
1104 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1106 break;
1107 case eCmd_AddRemote:
1109 CAddRemoteDlg(this).DoModal();
1110 Refresh();
1112 break;
1113 case eCmd_ManageRemotes:
1115 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS)), new CSettingGitRemote(g_Git.m_CurrentDir), this).DoModal();
1116 // CSettingGitRemote W_Remotes(m_cmdPath);
1117 // W_Remotes.DoModal();
1118 Refresh();
1120 break;
1121 case eCmd_CreateBranch:
1123 CString *commitHash = NULL;
1124 if (selectedLeafs.size() == 1)
1125 commitHash = &(selectedLeafs[0]->m_csRefHash);
1126 CAppUtils::CreateBranchTag(false, commitHash);
1127 Refresh();
1129 break;
1130 case eCmd_CreateTag:
1132 CAppUtils::CreateBranchTag(true);
1133 Refresh();
1135 break;
1136 case eCmd_DeleteAllTags:
1138 for (int i = 0; i < m_ListRefLeafs.GetItemCount(); ++i)
1140 m_ListRefLeafs.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
1141 selectedLeafs.push_back((CShadowTree*)m_ListRefLeafs.GetItemData(i));
1143 if (ConfirmDeleteRef(selectedLeafs))
1144 DoDeleteRefs(selectedLeafs, true);
1145 Refresh();
1147 break;
1148 case eCmd_Diff:
1150 CFileDiffDlg dlg;
1151 dlg.SetDiff(
1152 NULL,
1153 selectedLeafs[0]->m_csRefHash,
1154 selectedLeafs[1]->m_csRefHash);
1155 dlg.DoModal();
1157 break;
1158 case eCmd_EditBranchDescription:
1160 CInputDlg dlg;
1161 dlg.m_sHintText = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1162 dlg.m_sInputText = selectedLeafs[0]->m_csDescription;
1163 dlg.m_sTitle = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1164 dlg.m_bUseLogWidth = true;
1165 if(dlg.DoModal() == IDOK)
1167 CString key;
1168 key.Format(_T("branch.%s.description"), selectedLeafs[0]->m_csRefName);
1169 dlg.m_sInputText.Replace(_T("\r"), _T(""));
1170 dlg.m_sInputText.Trim();
1171 if (dlg.m_sInputText.IsEmpty())
1172 g_Git.UnsetConfigValue(key);
1173 else
1174 g_Git.SetConfigValue(key, dlg.m_sInputText);
1175 Refresh();
1178 break;
1182 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree& leafs, const wchar_t* from)
1184 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
1185 if(!(*i)->IsFrom(from))
1186 return false;
1187 return true;
1190 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
1192 if (pMsg->message == WM_KEYDOWN)
1194 switch (pMsg->wParam)
1196 /* case VK_RETURN:
1198 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1200 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1202 PostMessage(WM_COMMAND, IDOK);
1204 return TRUE;
1207 break;
1208 */ case VK_F2:
1210 if(pMsg->hwnd == m_ListRefLeafs.m_hWnd)
1212 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1213 if(pos != NULL)
1214 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1217 break;
1219 case VK_F5:
1221 Refresh();
1223 break;
1228 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
1231 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1233 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1234 *pResult = 0;
1236 if(m_currSortCol == pNMLV->iSubItem)
1237 m_currSortDesc = !m_currSortDesc;
1238 else
1240 m_currSortCol = pNMLV->iSubItem;
1241 m_currSortDesc = false;
1244 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
1245 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
1247 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
1250 void CBrowseRefsDlg::OnDestroy()
1252 m_pickedRef = GetSelectedRef(true, false);
1254 int maxcol = m_ColumnManager.GetColumnCount();
1255 for (int col = 0; col < maxcol; ++col)
1256 if (m_ColumnManager.IsVisible(col))
1257 m_ColumnManager.ColumnResized(col);
1258 m_ColumnManager.WriteSettings();
1260 CResizableStandAloneDialog::OnDestroy();
1263 void CBrowseRefsDlg::OnItemChangedListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1265 LPNMLISTVIEW pNMListView = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1266 *pResult = 0;
1268 CShadowTree *item = (CShadowTree*)m_ListRefLeafs.GetItemData(pNMListView->iItem);
1269 if (item && pNMListView->uNewState == 2)
1270 m_sLastSelected = item->GetRefName();
1273 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR * /*pNMHDR*/, LRESULT *pResult)
1275 *pResult = 0;
1277 EndDialog(IDOK);
1280 CString CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef, int pickRef_Kind, bool pickMultipleRefs)
1282 CBrowseRefsDlg dlg(CString(),NULL);
1284 if(initialRef.IsEmpty())
1285 initialRef = L"HEAD";
1286 dlg.m_initialRef = initialRef;
1287 dlg.m_pickRef_Kind = pickRef_Kind;
1288 dlg.m_bPickOne = !pickMultipleRefs;
1290 if(dlg.DoModal() != IDOK)
1291 return CString();
1293 return dlg.m_pickedRef;
1296 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)
1298 CString origRef;
1299 pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);
1300 CString resultRef = PickRef(false,origRef,pickRef_Kind);
1301 if(resultRef.IsEmpty())
1302 return false;
1303 if(wcsncmp(resultRef,L"refs/",5)==0)
1304 resultRef = resultRef.Mid(5);
1305 // if(wcsncmp(resultRef,L"heads/",6)==0)
1306 // resultRef = resultRef.Mid(6);
1308 //Find closest match of choice in combobox
1309 int ixFound = -1;
1310 int matchLength = 0;
1311 CString comboRefName;
1312 for(int i = 0; i < pComboBox->GetCount(); ++i)
1314 pComboBox->GetLBText(i, comboRefName);
1315 if(comboRefName.Find(L'/') < 0 && !comboRefName.IsEmpty())
1316 comboRefName.Insert(0,L"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1317 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)
1319 matchLength = comboRefName.GetLength();
1320 ixFound = i;
1323 if(ixFound >= 0)
1324 pComboBox->SetCurSel(ixFound);
1325 else
1326 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)
1328 return true;
1331 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1333 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1334 *pResult = FALSE;
1336 if(pDispInfo->item.pszText == NULL)
1337 return; //User canceled changing
1339 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1341 if(!pTree->IsFrom(L"refs/heads"))
1343 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES, IDS_APPNAME, MB_OK | MB_ICONERROR);
1344 return;
1347 CString origName = pTree->GetRefName().Mid(11);
1349 CString newName;
1350 if(m_pListCtrlRoot != NULL)
1351 newName = m_pListCtrlRoot->GetRefName() + L'/';
1352 newName += pDispInfo->item.pszText;
1354 if(wcsncmp(newName,L"refs/heads/",11)!=0)
1356 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE, IDS_APPNAME, MB_OK | MB_ICONERROR);
1357 return;
1360 CString newNameTrunced = newName.Mid(11);
1362 CString errorMsg;
1363 if(g_Git.Run(L"git branch -m \"" + origName + L"\" \"" + newNameTrunced + L"\"", &errorMsg, CP_UTF8) != 0)
1365 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1366 return;
1368 //Do as if it failed to rename. Let Refresh() do the job.
1369 //*pResult = TRUE;
1371 Refresh(newName);
1373 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1375 // AfxMessageBox(W_csPopup);
1379 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1381 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1382 *pResult = FALSE;
1384 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1386 if(!pTree->IsFrom(L"refs/heads"))
1388 *pResult = TRUE; //Dont allow renaming any other things then branches at the moment.
1389 return;
1393 void CBrowseRefsDlg::OnEnChangeEditFilter()
1395 SetTimer(IDT_FILTER, 1000, NULL);
1398 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent)
1400 if (nIDEvent == IDT_FILTER)
1402 KillTimer(IDT_FILTER);
1403 FillListCtrlForTreeNode(m_RefTreeCtrl.GetSelectedItem());
1406 CResizableStandAloneDialog::OnTimer(nIDEvent);
1409 LRESULT CBrowseRefsDlg::OnClickedInfoIcon(WPARAM /*wParam*/, LPARAM lParam)
1411 // FIXME: x64 version would get this function called with unexpected parameters.
1412 if (!lParam)
1413 return 0;
1415 RECT * rect = (LPRECT)lParam;
1416 CPoint point;
1417 CString temp;
1418 point = CPoint(rect->left, rect->bottom);
1419 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1420 CMenu popup;
1421 if (popup.CreatePopupMenu())
1423 temp.LoadString(IDS_LOG_FILTER_REFNAME);
1424 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME), LOGFILTER_REFNAME, temp);
1426 temp.LoadString(IDS_LOG_FILTER_SUBJECT);
1427 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT), LOGFILTER_SUBJECT, temp);
1429 temp.LoadString(IDS_LOG_FILTER_AUTHORS);
1430 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS), LOGFILTER_AUTHORS, temp);
1432 temp.LoadString(IDS_LOG_FILTER_REVS);
1433 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS), LOGFILTER_REVS, temp);
1435 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1436 if (selection != 0)
1438 m_SelectedFilters ^= selection;
1439 SetFilterCueText();
1440 SetTimer(IDT_FILTER, 1000, NULL);
1443 return 0L;
1446 void CBrowseRefsDlg::SetFilterCueText()
1448 CString temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY));
1449 temp += _T(" ");
1451 if (m_SelectedFilters & LOGFILTER_REFNAME)
1452 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME));
1454 if (m_SelectedFilters & LOGFILTER_SUBJECT)
1456 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1457 temp += _T(", ");
1458 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT));
1461 if (m_SelectedFilters & LOGFILTER_AUTHORS)
1463 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1464 temp += _T(", ");
1465 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS));
1468 if (m_SelectedFilters & LOGFILTER_REVS)
1470 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1471 temp += _T(", ");
1472 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS));
1475 // to make the cue banner text appear more to the right of the edit control
1476 temp = _T(" ") + temp;
1477 m_ctrlFilter.SetCueBanner(temp.TrimRight());