Add option 'Show complete log'
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blobf4320a8efc7cb9ad659bdf3710bbe5734d650696
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),
163 m_bPickCurrentBranch(false)
168 CBrowseRefsDlg::~CBrowseRefsDlg()
172 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)
174 CDialog::DoDataExchange(pDX);
175 DDX_Control(pDX, IDC_TREE_REF, m_RefTreeCtrl);
176 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_ListRefLeafs);
177 DDX_Control(pDX, IDC_BROWSEREFS_EDIT_FILTER, m_ctrlFilter);
181 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)
182 ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)
183 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)
184 ON_WM_CONTEXTMENU()
185 ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)
186 ON_WM_DESTROY()
187 ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)
188 ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnItemChangedListRefLeafs)
189 ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs)
190 ON_NOTIFY(LVN_BEGINLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs)
191 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER, &CBrowseRefsDlg::OnEnChangeEditFilter)
192 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED, OnClickedInfoIcon)
193 ON_WM_TIMER()
194 ON_BN_CLICKED(IDC_CURRENTBRANCH, OnBnClickedCurrentbranch)
195 END_MESSAGE_MAP()
198 // CBrowseRefsDlg message handlers
200 void CBrowseRefsDlg::OnBnClickedOk()
202 OnOK();
205 BOOL CBrowseRefsDlg::OnInitDialog()
207 CResizableStandAloneDialog::OnInitDialog();
208 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
210 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
211 m_ctrlFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
212 m_ctrlFilter.SetInfoIcon(IDI_FILTEREDIT);
213 SetFilterCueText();
215 AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);
216 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
217 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER, TOP_LEFT);
218 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER, TOP_LEFT, TOP_RIGHT);
219 AddAnchor(IDHELP, BOTTOM_RIGHT);
221 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
222 static UINT columnNames[] = { IDS_BRANCHNAME, IDS_DATELASTCOMMIT, IDS_LASTCOMMIT, IDS_LASTAUTHOR, IDS_HASH, IDS_DESCRIPTION };
223 static int columnWidths[] = { 150, 100, 300, 100, 80, 80 };
224 DWORD dwDefaultColumns = (1 << eCol_Name) | (1 << eCol_Date) | (1 << eCol_Msg) |
225 (1 << eCol_LastAuthor) | (1 << eCol_Hash) | (1 << eCol_Description);
226 m_ColumnManager.SetNames(columnNames, _countof(columnNames));
227 m_ColumnManager.ReadSettings(dwDefaultColumns, 0, _T("BrowseRefs"), _countof(columnNames), columnWidths);
228 m_bPickCurrentBranch = false;
230 AddAnchor(IDOK,BOTTOM_RIGHT);
231 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
233 Refresh(m_initialRef);
235 EnableSaveRestore(L"BrowseRefs");
237 CString sWindowTitle;
238 GetWindowText(sWindowTitle);
239 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
241 m_bHasWC = !g_GitAdminDir.IsBareRepo(g_Git.m_CurrentDir);
243 if (m_bPickOne)
244 m_ListRefLeafs.ModifyStyle(0, LVS_SINGLESEL);
246 m_ListRefLeafs.SetFocus();
247 return FALSE;
250 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
252 int posSlash=nameLeft.Find('/');
253 CString nameSub;
254 if(posSlash<0)
256 nameSub=nameLeft;
257 nameLeft.Empty();//Nothing left
259 else
261 nameSub=nameLeft.Left(posSlash);
262 nameLeft=nameLeft.Mid(posSlash+1);
264 if(nameSub.IsEmpty())
265 return NULL;
267 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
268 return NULL;
270 CShadowTree& nextNode=m_ShadowTree[nameSub];
271 nextNode.m_csRefName=nameSub;
272 nextNode.m_pParent=this;
273 return &nextNode;
276 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)
278 if(IsLeaf())
280 if(m_csRefName.GetLength() > partialRefName.GetLength())
281 return NULL;
282 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)
284 //Match of leaf name. Try match on total name.
285 CString totalRefName = GetRefName();
286 if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)
287 return this; //Also match. Found.
290 else
292 //Not a leaf. Search all nodes.
293 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)
295 CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);
296 if(pSubtree != NULL)
297 return pSubtree; //Found
300 return NULL;//Not found
304 typedef std::map<CString,CString> MAP_STRING_STRING;
306 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf, bool pickFirstSelIfMultiSel)
308 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
309 //List ctrl selection?
310 if(pos && (pickFirstSelIfMultiSel || m_ListRefLeafs.GetSelectedCount() == 1))
312 //A leaf is selected
313 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(
314 m_ListRefLeafs.GetNextSelectedItem(pos));
315 return pTree->GetRefName();
317 else if (pos && !pickFirstSelIfMultiSel)
319 // at least one leaf is selected
320 CString refs;
321 int index;
322 while ((index = m_ListRefLeafs.GetNextSelectedItem(pos)) >= 0)
324 CString ref = ((CShadowTree*)m_ListRefLeafs.GetItemData(index))->GetRefName();
325 if(wcsncmp(ref, L"refs/", 5) == 0)
326 ref = ref.Mid(5);
327 if(wcsncmp(ref, L"heads/", 6) == 0)
328 ref = ref.Mid(6);
329 refs += ref + _T(" ");
331 return refs.Trim();
333 else if(!onlyIfLeaf)
335 //Tree ctrl selection?
336 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
337 if(hTree!=NULL)
339 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);
340 return pTree->GetRefName();
343 return CString();//None
346 static int GetBranchDescriptionsCallback(const git_config_entry *entry, void *data)
348 MAP_STRING_STRING *descriptions = (MAP_STRING_STRING *) data;
349 CString key = CUnicodeUtils::GetUnicode(entry->name, CP_UTF8);
350 CString val = CUnicodeUtils::GetUnicode(entry->value, CP_UTF8);
351 descriptions->insert(std::make_pair(key.Mid(7, key.GetLength() - 7 - 12), val)); // 7: branch., 12: .description
352 return 0;
355 MAP_STRING_STRING GetBranchDescriptions()
357 MAP_STRING_STRING descriptions;
358 git_config * config;
359 git_config_new(&config);
360 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL, FALSE);
361 git_config_foreach_match(config, "branch\\..*\\.description", GetBranchDescriptionsCallback, &descriptions);
362 git_config_free(config);
363 return descriptions;
366 void CBrowseRefsDlg::Refresh(CString selectRef)
368 // m_RefMap.clear();
369 // g_Git.GetMapHashToFriendName(m_RefMap);
371 if(!selectRef.IsEmpty())
373 if(selectRef == "HEAD")
375 selectRef = g_Git.GetSymbolicRef(selectRef, false);
378 else
380 selectRef = GetSelectedRef(false, true);
383 m_RefTreeCtrl.DeleteAllItems();
384 m_ListRefLeafs.DeleteAllItems();
385 m_TreeRoot.m_ShadowTree.clear();
386 m_TreeRoot.m_csRefName = "refs";
387 m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);
388 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);
390 CString allRefs, error;
391 if (g_Git.Run(L"git.exe for-each-ref --format="
392 L"%(refname)%04"
393 L"%(objectname)%04"
394 L"%(authordate:relative)%04"
395 L"%(subject)%04"
396 L"%(authorname)%04"
397 L"%(authordate:iso8601)%03",
398 &allRefs, &error, CP_UTF8))
400 CMessageBox::Show(NULL, CString(_T("Get refs failed\n")) + error, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
403 int linePos=0;
404 CString singleRef;
406 MAP_STRING_STRING refMap;
408 //First sort on ref name
409 while(!(singleRef=allRefs.Tokenize(L"\03",linePos)).IsEmpty())
411 singleRef.TrimLeft(L"\r\n");
412 int valuePos=0;
413 CString refName=singleRef.Tokenize(L"\04",valuePos);
414 if(refName.IsEmpty())
415 continue;
416 CString refRest=singleRef.Mid(valuePos);
419 //Use ref based on m_pickRef_Kind
420 if (wcsncmp(refName, L"refs/heads/", 11) == 0 && !(m_pickRef_Kind & gPickRef_Head))
421 continue; //Skip
422 if (wcsncmp(refName, L"refs/tags/", 10) == 0 && !(m_pickRef_Kind & gPickRef_Tag))
423 continue; //Skip
424 if (wcsncmp(refName, L"refs/remotes/", 13) == 0 && !(m_pickRef_Kind & gPickRef_Remote))
425 continue; //Skip
427 refMap[refName] = refRest; //Use
430 MAP_STRING_STRING descriptions = GetBranchDescriptions();
432 //Populate ref tree
433 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
435 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
436 CString values=iterRefMap->second;
437 values.Replace(L"\04" L"\04",L"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
439 int valuePos=0;
440 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
441 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
442 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
443 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
444 treeLeaf.m_csDate_Iso8601= values.Tokenize(L"\04",valuePos);
446 if (wcsncmp(iterRefMap->first, L"refs/heads/", 11) == 0)
447 treeLeaf.m_csDescription = descriptions[treeLeaf.m_csRefName];
450 // try exact match first
451 if(selectRef.IsEmpty() || !(SelectRef(selectRef, true) || SelectRef(selectRef, false)))
452 //Probably not on a branch. Select root node.
453 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);
457 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
459 if(!bExactMatch)
461 CString newRefName = GetFullRefName(refName);
462 if(!newRefName.IsEmpty())
463 refName = newRefName;
464 //else refName is not a valid ref. Try to select as good as possible.
466 if(_wcsnicmp(refName, L"refs/", 5) != 0)
467 return false; // Not a ref name
469 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
470 if(treeLeafHead.m_hTree != NULL)
472 //Not a leaf. Select tree node and return
473 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
474 return true;
477 if(treeLeafHead.m_pParent==NULL)
478 return false; //Weird... should not occur.
480 //This is the current head.
481 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
483 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
485 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
486 if(pCurrShadowTree == &treeLeafHead)
488 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
489 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
493 return true;
496 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
498 if(pTreePos==NULL)
500 if(_wcsnicmp(refName, L"refs/", 5) == 0)
501 refName=refName.Mid(5);
502 pTreePos=&m_TreeRoot;
504 if(refName.IsEmpty())
505 return *pTreePos;//Found leaf
507 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
508 if(pNextTree==NULL)
510 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
511 ASSERT(!bCreateIfNotExist);
512 return *pTreePos;
515 if(!refName.IsEmpty())
517 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
518 //Leafs are for the list control.
519 if(pNextTree->m_hTree==NULL)
521 //New tree. Create node in control.
522 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
523 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
527 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
531 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
533 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
534 *pResult = 0;
536 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
539 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
541 m_ListRefLeafs.DeleteAllItems();
543 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
544 if(pTree==NULL)
546 ASSERT(FALSE);
547 return;
549 FillListCtrlForShadowTree(pTree,L"",true);
552 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
554 if(pTree->IsLeaf())
556 CString filter;
557 m_ctrlFilter.GetWindowText(filter);
558 filter.MakeLower();
559 CString ref = refNamePrefix + pTree->m_csRefName;
560 if (!(pTree->m_csRefName.IsEmpty() || pTree->m_csRefName == "refs" && pTree->m_pParent == NULL) && IsMatchFilter(pTree, ref, filter))
562 int indexItem = m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(), L"");
564 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
565 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, ref);
566 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);
567 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
568 m_ListRefLeafs.SetItemText(indexItem,eCol_LastAuthor, pTree->m_csAuthor);
569 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
570 int pos = 0;
571 m_ListRefLeafs.SetItemText(indexItem,eCol_Description, pTree->m_csDescription.Tokenize(_T("\n"), pos));
574 else
577 CString csThisName;
578 if(!isFirstLevel)
579 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
580 else
581 m_pListCtrlRoot = pTree;
582 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
584 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
587 if (isFirstLevel)
589 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
590 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
592 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
596 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree* pTree, const CString &ref, const CString &filter)
598 if (m_SelectedFilters & LOGFILTER_REFNAME)
600 CString msg = ref;
601 msg = msg.MakeLower();
603 if (msg.Find(filter) >= 0)
604 return true;
607 if (m_SelectedFilters & LOGFILTER_SUBJECT)
609 CString msg = pTree->m_csSubject;
610 msg = msg.MakeLower();
612 if (msg.Find(filter) >= 0)
613 return true;
616 if (m_SelectedFilters & LOGFILTER_AUTHORS)
618 CString msg = pTree->m_csAuthor;
619 msg = msg.MakeLower();
621 if (msg.Find(filter) >= 0)
622 return true;
625 if (m_SelectedFilters & LOGFILTER_REVS)
627 CString msg = pTree->m_csRefHash;
628 msg = msg.MakeLower();
630 if (msg.Find(filter) >= 0)
631 return true;
633 return false;
636 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree& leafs)
638 ASSERT(!leafs.empty());
640 CString csMessage;
641 UINT mbIcon=MB_ICONQUESTION;
643 bool bIsRemoteBranch = false;
644 bool bIsBranch = false;
645 if (leafs[0]->IsFrom(L"refs/remotes/")) {bIsBranch = true; bIsRemoteBranch = true;}
646 else if (leafs[0]->IsFrom(L"refs/heads/")) {bIsBranch = true;}
648 if(bIsBranch)
650 if(leafs.size() == 1)
652 CString branchToDelete = leafs[0]->GetRefName().Mid(bIsRemoteBranch ? 13 : 11);
653 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, branchToDelete);
655 //Check if branch is fully merged in HEAD
656 if (!g_Git.IsFastForward(leafs[0]->GetRefName(), _T("HEAD")))
658 csMessage += L"\r\n\r\n";
659 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED));
660 mbIcon = MB_ICONWARNING;
663 if(bIsRemoteBranch)
665 csMessage += L"\r\n\r\n";
666 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
667 mbIcon = MB_ICONWARNING;
670 else
672 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
674 csMessage += L"\r\n\r\n";
675 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK));
676 mbIcon = MB_ICONWARNING;
678 if(bIsRemoteBranch)
680 csMessage += L"\r\n\r\n";
681 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
682 mbIcon = MB_ICONWARNING;
687 else if(leafs[0]->IsFrom(L"refs/tags/"))
689 if(leafs.size() == 1)
691 CString tagToDelete = leafs[0]->GetRefName().Mid(10);
692 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, tagToDelete);
694 else
696 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
700 return CMessageBox::Show(m_hWnd, csMessage, _T("TortoiseGit"), MB_YESNO | mbIcon) == IDYES;
704 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree& leafs, bool bForce)
706 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
707 if(!DoDeleteRef((*i)->GetRefName(), bForce))
708 return false;
709 return true;
712 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)
714 bool bIsRemoteBranch = false;
715 bool bIsBranch = false;
716 if (wcsncmp(completeRefName, L"refs/remotes/",13) == 0) {bIsBranch = true; bIsRemoteBranch = true;}
717 else if (wcsncmp(completeRefName, L"refs/heads/",11) == 0) {bIsBranch = true;}
719 if(bIsBranch)
721 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);
722 CString cmd;
723 if(bIsRemoteBranch)
725 CString remoteName, remoteBranchToDelete;
726 if (SplitRemoteBranchName(branchToDelete, remoteName, remoteBranchToDelete))
727 return false;
729 if(CAppUtils::IsSSHPutty())
731 CAppUtils::LaunchPAgent(NULL, &remoteName);
734 cmd.Format(L"git.exe push \"%s\" :refs/heads/%s", remoteName, remoteBranchToDelete);
736 else
737 cmd.Format(L"git.exe branch -%c -- %s",bForce?L'D':L'd',branchToDelete);
738 CSysProgressDlg sysProgressDlg;
739 if (bIsRemoteBranch)
741 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
742 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS)));
743 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
744 sysProgressDlg.SetShowProgressBar(false);
745 sysProgressDlg.ShowModal(this, true);
747 CString errorMsg;
748 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
750 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
751 if (bIsRemoteBranch)
752 sysProgressDlg.Stop();
753 BringWindowToTop();
754 return false;
756 if (bIsRemoteBranch)
757 sysProgressDlg.Stop();
758 BringWindowToTop();
760 else if (wcsncmp(completeRefName, L"refs/tags/", 10) == 0)
762 CString tagToDelete = completeRefName.Mid(10);
763 CString cmd;
764 cmd.Format(L"git.exe tag -d -- %s",tagToDelete);
765 CString errorMsg;
766 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
768 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
769 return false;
772 return true;
775 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
777 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
778 if(pLeaf == NULL)
779 return CString();
780 return pLeaf->GetRefName();
784 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
786 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
787 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
790 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
792 CPoint clientPoint=point;
793 m_RefTreeCtrl.ScreenToClient(&clientPoint);
795 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
796 if(hTreeItem!=NULL)
797 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
799 VectorPShadowTree tree;
800 ShowContextMenu(point,hTreeItem,tree);
804 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
806 std::vector<CShadowTree*> selectedLeafs;
807 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
808 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
809 while(pos)
811 selectedLeafs.push_back(
812 (CShadowTree*)m_ListRefLeafs.GetItemData(
813 m_ListRefLeafs.GetNextSelectedItem(pos)));
816 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
819 CString GetTwoSelectedRefs(VectorPShadowTree& selectedLeafs, const CString &lastSelected, const CString &separator)
821 ASSERT(selectedLeafs.size() == 2);
823 if (selectedLeafs.at(0)->GetRefName() == lastSelected)
824 return g_Git.StripRefName(selectedLeafs.at(1)->GetRefName()) + separator + g_Git.StripRefName(lastSelected);
825 else
826 return g_Git.StripRefName(selectedLeafs.at(0)->GetRefName()) + separator + g_Git.StripRefName(lastSelected);
829 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
831 CIconMenu popupMenu;
832 popupMenu.CreatePopupMenu();
834 bool bAddSeparator = false;
835 CString remoteName;
837 if(selectedLeafs.size()==1)
839 bAddSeparator = true;
841 bool bShowReflogOption = false;
842 bool bShowFetchOption = 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 bShowRenameOption = true;
853 bShowEditBranchDescriptionOption = true;
855 else if(selectedLeafs[0]->IsFrom(L"refs/remotes/"))
857 bShowReflogOption = true;
858 bShowFetchOption = true;
859 bShowCreateBranchOption = true;
861 CString remoteBranch;
862 if (SplitRemoteBranchName(selectedLeafs[0]->GetRefName(), remoteName, remoteBranch))
863 bShowFetchOption = false;
864 else
865 fetchFromCmd.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
867 else if(selectedLeafs[0]->IsFrom(L"refs/tags/"))
871 CString temp;
872 temp.LoadString(IDS_MENULOG);
873 popupMenu.AppendMenuIcon(eCmd_ViewLog, temp, IDI_LOG);
874 popupMenu.AppendMenuIcon(eCmd_RepoBrowser, IDS_LOG_BROWSEREPO, IDI_REPOBROWSE);
875 if(bShowReflogOption)
877 temp.LoadString(IDS_MENUREFLOG);
878 popupMenu.AppendMenuIcon(eCmd_ShowReflog, temp, IDI_LOG);
881 popupMenu.AppendMenu(MF_SEPARATOR);
882 bAddSeparator = false;
884 if(bShowFetchOption)
886 bAddSeparator = true;
887 popupMenu.AppendMenuIcon(eCmd_Fetch, fetchFromCmd, IDI_PULL);
890 if(bAddSeparator)
891 popupMenu.AppendMenu(MF_SEPARATOR);
893 bAddSeparator = false;
894 if (m_bHasWC)
896 CString format, str;
897 if (selectedLeafs[0]->GetRefName() != _T("refs/heads/") + g_Git.GetCurrentBranch())
899 format.LoadString(IDS_LOG_POPUP_MERGEREV);
900 str.Format(format, g_Git.GetCurrentBranch());
901 popupMenu.AppendMenuIcon(eCmd_Merge, str, IDI_MERGE);
903 popupMenu.AppendMenuIcon(eCmd_Switch, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS)), IDI_SWITCH);
904 popupMenu.AppendMenu(MF_SEPARATOR);
907 if(bShowCreateBranchOption)
909 bAddSeparator = true;
910 temp.LoadString(IDS_MENUBRANCH);
911 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
914 if (bShowEditBranchDescriptionOption)
916 bAddSeparator = true;
917 popupMenu.AppendMenuIcon(eCmd_EditBranchDescription, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION)), IDI_RENAME);
919 if(bShowRenameOption)
921 bAddSeparator = true;
922 popupMenu.AppendMenuIcon(eCmd_Rename, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME)), IDI_RENAME);
925 else if(selectedLeafs.size() == 2)
927 bAddSeparator = true;
928 popupMenu.AppendMenuIcon(eCmd_Diff, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS)), IDI_DIFF);
929 popupMenu.AppendMenuIcon(eCmd_UnifiedDiff, CString(MAKEINTRESOURCE(IDS_LOG_POPUP_GNUDIFF)), 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[1]->GetRefName() + L"^{}",
1154 selectedLeafs[0]->GetRefName() + L"^{}");
1155 dlg.DoModal();
1157 break;
1158 case eCmd_UnifiedDiff:
1160 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), selectedLeafs[0]->m_csRefHash, CTGitPath(), selectedLeafs[1]->m_csRefHash);
1162 break;
1163 case eCmd_EditBranchDescription:
1165 CInputDlg dlg;
1166 dlg.m_sHintText = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1167 dlg.m_sInputText = selectedLeafs[0]->m_csDescription;
1168 dlg.m_sTitle = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1169 dlg.m_bUseLogWidth = true;
1170 if(dlg.DoModal() == IDOK)
1172 CString key;
1173 key.Format(_T("branch.%s.description"), selectedLeafs[0]->m_csRefName);
1174 dlg.m_sInputText.Replace(_T("\r"), _T(""));
1175 dlg.m_sInputText.Trim();
1176 if (dlg.m_sInputText.IsEmpty())
1177 g_Git.UnsetConfigValue(key);
1178 else
1179 g_Git.SetConfigValue(key, dlg.m_sInputText);
1180 Refresh();
1183 break;
1187 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree& leafs, const wchar_t* from)
1189 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
1190 if(!(*i)->IsFrom(from))
1191 return false;
1192 return true;
1195 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
1197 if (pMsg->message == WM_KEYDOWN)
1199 switch (pMsg->wParam)
1201 /* case VK_RETURN:
1203 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1205 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1207 PostMessage(WM_COMMAND, IDOK);
1209 return TRUE;
1212 break;
1213 */ case VK_F2:
1215 if(pMsg->hwnd == m_ListRefLeafs.m_hWnd)
1217 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1218 if(pos != NULL)
1219 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1222 break;
1224 case VK_F5:
1226 Refresh();
1228 break;
1233 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
1236 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1238 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1239 *pResult = 0;
1241 if(m_currSortCol == pNMLV->iSubItem)
1242 m_currSortDesc = !m_currSortDesc;
1243 else
1245 m_currSortCol = pNMLV->iSubItem;
1246 m_currSortDesc = false;
1249 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
1250 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
1252 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
1255 void CBrowseRefsDlg::OnDestroy()
1257 if (m_bPickCurrentBranch)
1258 m_pickedRef = g_Git.GetCurrentBranch(true);
1259 else
1260 m_pickedRef = GetSelectedRef(true, false);
1262 int maxcol = m_ColumnManager.GetColumnCount();
1263 for (int col = 0; col < maxcol; ++col)
1264 if (m_ColumnManager.IsVisible(col))
1265 m_ColumnManager.ColumnResized(col);
1266 m_ColumnManager.WriteSettings();
1268 CResizableStandAloneDialog::OnDestroy();
1271 void CBrowseRefsDlg::OnItemChangedListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1273 LPNMLISTVIEW pNMListView = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1274 *pResult = 0;
1276 CShadowTree *item = (CShadowTree*)m_ListRefLeafs.GetItemData(pNMListView->iItem);
1277 if (item && pNMListView->uNewState == 2)
1278 m_sLastSelected = item->GetRefName();
1281 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR * /*pNMHDR*/, LRESULT *pResult)
1283 *pResult = 0;
1285 EndDialog(IDOK);
1288 CString CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef, int pickRef_Kind, bool pickMultipleRefs)
1290 CBrowseRefsDlg dlg(CString(),NULL);
1292 if(initialRef.IsEmpty())
1293 initialRef = L"HEAD";
1294 dlg.m_initialRef = initialRef;
1295 dlg.m_pickRef_Kind = pickRef_Kind;
1296 dlg.m_bPickOne = !pickMultipleRefs;
1298 if(dlg.DoModal() != IDOK)
1299 return CString();
1301 return dlg.m_pickedRef;
1304 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)
1306 CString origRef;
1307 pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);
1308 CString resultRef = PickRef(false,origRef,pickRef_Kind);
1309 if(resultRef.IsEmpty())
1310 return false;
1311 if(wcsncmp(resultRef,L"refs/",5)==0)
1312 resultRef = resultRef.Mid(5);
1313 // if(wcsncmp(resultRef,L"heads/",6)==0)
1314 // resultRef = resultRef.Mid(6);
1316 //Find closest match of choice in combobox
1317 int ixFound = -1;
1318 int matchLength = 0;
1319 CString comboRefName;
1320 for(int i = 0; i < pComboBox->GetCount(); ++i)
1322 pComboBox->GetLBText(i, comboRefName);
1323 if(comboRefName.Find(L'/') < 0 && !comboRefName.IsEmpty())
1324 comboRefName.Insert(0,L"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1325 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)
1327 matchLength = comboRefName.GetLength();
1328 ixFound = i;
1331 if(ixFound >= 0)
1332 pComboBox->SetCurSel(ixFound);
1333 else
1334 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)
1336 return true;
1339 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1341 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1342 *pResult = FALSE;
1344 if(pDispInfo->item.pszText == NULL)
1345 return; //User canceled changing
1347 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1349 if(!pTree->IsFrom(L"refs/heads/"))
1351 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES, IDS_APPNAME, MB_OK | MB_ICONERROR);
1352 return;
1355 CString origName = pTree->GetRefName().Mid(11);
1357 CString newName;
1358 if(m_pListCtrlRoot != NULL)
1359 newName = m_pListCtrlRoot->GetRefName() + L'/';
1360 newName += pDispInfo->item.pszText;
1362 if(wcsncmp(newName,L"refs/heads/",11)!=0)
1364 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE, IDS_APPNAME, MB_OK | MB_ICONERROR);
1365 return;
1368 CString newNameTrunced = newName.Mid(11);
1370 CString errorMsg;
1371 if(g_Git.Run(L"git.exe branch -m \"" + origName + L"\" \"" + newNameTrunced + L"\"", &errorMsg, CP_UTF8) != 0)
1373 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1374 return;
1376 //Do as if it failed to rename. Let Refresh() do the job.
1377 //*pResult = TRUE;
1379 Refresh(newName);
1381 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1383 // AfxMessageBox(W_csPopup);
1387 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1389 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1390 *pResult = FALSE;
1392 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1394 if(!pTree->IsFrom(L"refs/heads/"))
1396 *pResult = TRUE; //Dont allow renaming any other things then branches at the moment.
1397 return;
1401 void CBrowseRefsDlg::OnEnChangeEditFilter()
1403 SetTimer(IDT_FILTER, 1000, NULL);
1406 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent)
1408 if (nIDEvent == IDT_FILTER)
1410 KillTimer(IDT_FILTER);
1411 FillListCtrlForTreeNode(m_RefTreeCtrl.GetSelectedItem());
1414 CResizableStandAloneDialog::OnTimer(nIDEvent);
1417 LRESULT CBrowseRefsDlg::OnClickedInfoIcon(WPARAM /*wParam*/, LPARAM lParam)
1419 // FIXME: x64 version would get this function called with unexpected parameters.
1420 if (!lParam)
1421 return 0;
1423 RECT * rect = (LPRECT)lParam;
1424 CPoint point;
1425 CString temp;
1426 point = CPoint(rect->left, rect->bottom);
1427 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1428 CMenu popup;
1429 if (popup.CreatePopupMenu())
1431 temp.LoadString(IDS_LOG_FILTER_REFNAME);
1432 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME), LOGFILTER_REFNAME, temp);
1434 temp.LoadString(IDS_LOG_FILTER_SUBJECT);
1435 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT), LOGFILTER_SUBJECT, temp);
1437 temp.LoadString(IDS_LOG_FILTER_AUTHORS);
1438 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS), LOGFILTER_AUTHORS, temp);
1440 temp.LoadString(IDS_LOG_FILTER_REVS);
1441 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS), LOGFILTER_REVS, temp);
1443 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1444 if (selection != 0)
1446 m_SelectedFilters ^= selection;
1447 SetFilterCueText();
1448 SetTimer(IDT_FILTER, 1000, NULL);
1451 return 0L;
1454 void CBrowseRefsDlg::SetFilterCueText()
1456 CString temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY));
1457 temp += _T(" ");
1459 if (m_SelectedFilters & LOGFILTER_REFNAME)
1460 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME));
1462 if (m_SelectedFilters & LOGFILTER_SUBJECT)
1464 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1465 temp += _T(", ");
1466 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT));
1469 if (m_SelectedFilters & LOGFILTER_AUTHORS)
1471 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1472 temp += _T(", ");
1473 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS));
1476 if (m_SelectedFilters & LOGFILTER_REVS)
1478 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1479 temp += _T(", ");
1480 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS));
1483 // to make the cue banner text appear more to the right of the edit control
1484 temp = _T(" ") + temp;
1485 m_ctrlFilter.SetCueBanner(temp.TrimRight());
1488 void CBrowseRefsDlg::OnBnClickedCurrentbranch()
1490 m_bPickCurrentBranch = true;
1491 OnOK();