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
23 #include "TortoiseProc.h"
24 #include "BrowseRefsDlg.h"
26 #include "AddRemoteDlg.h"
28 #include "Settings\SettingGitRemote.h"
29 #include "SinglePropSheetDlg.h"
30 #include "MessageBox.h"
31 #include "RefLogDlg.h"
33 #include "FileDiffDlg.h"
34 #include "DeleteRemoteTagDlg.h"
35 #include "UnicodeUtils.h"
37 #include "SysProgressDlg.h"
39 static int SplitRemoteBranchName(CString ref
, CString
&remote
, CString
&branch
)
41 if (ref
.Left(13) == _T("refs/remotes/"))
43 else if (ref
.Left(8) == _T("remotes/"))
47 int result
= g_Git
.GetRemoteList(list
);
51 for (size_t i
= 0; i
< list
.size(); ++i
)
53 if (ref
.Left(list
[i
].GetLength() + 1) == list
[i
] + _T("/"))
56 branch
= ref
.Mid(list
[i
].GetLength() + 1);
70 void SetSortArrow(CListCtrl
* control
, int nColumn
, bool bAscending
)
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
);
86 pHeader
->GetItem(nColumn
, &HeaderItem
);
87 HeaderItem
.fmt
|= (bAscending
? HDF_SORTUP
: HDF_SORTDOWN
);
88 pHeader
->SetItem(nColumn
, &HeaderItem
);
92 class CRefLeafListCompareFunc
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
);
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
)
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
);
121 int CompareNoDesc(CShadowTree
* pLeft
, CShadowTree
* pRight
)
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
);
134 int SortStrCmp(CString left
, CString right
)
137 return StrCmpLogicalW(left
, right
);
138 return StrCmpI(left
, right
);
147 // CBrowseRefsDlg dialog
149 IMPLEMENT_DYNAMIC(CBrowseRefsDlg
, CResizableStandAloneDialog
)
151 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath
, CWnd
* pParent
/*=NULL*/)
152 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD
, pParent
),
155 m_currSortDesc(false),
156 m_initialRef(L
"HEAD"),
157 m_pickRef_Kind(gPickRef_All
),
158 m_pListCtrlRoot(NULL
),
160 m_SelectedFilters(LOGFILTER_ALL
),
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
)
183 ON_NOTIFY(LVN_COLUMNCLICK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs
)
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
)
195 // CBrowseRefsDlg message handlers
197 void CBrowseRefsDlg::OnBnClickedOk()
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
);
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
);
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
);
247 m_ListRefLeafs
.ModifyStyle(0, LVS_SINGLESEL
);
249 m_ListRefLeafs
.SetFocus();
253 CShadowTree
* CShadowTree::GetNextSub(CString
& nameLeft
, bool bCreateIfNotExist
)
255 int posSlash
=nameLeft
.Find('/');
260 nameLeft
.Empty();//Nothing left
264 nameSub
=nameLeft
.Left(posSlash
);
265 nameLeft
=nameLeft
.Mid(posSlash
+1);
267 if(nameSub
.IsEmpty())
270 if(!bCreateIfNotExist
&& m_ShadowTree
.find(nameSub
)==m_ShadowTree
.end())
273 CShadowTree
& nextNode
=m_ShadowTree
[nameSub
];
274 nextNode
.m_csRefName
=nameSub
;
275 nextNode
.m_pParent
=this;
279 CShadowTree
* CShadowTree::FindLeaf(CString partialRefName
)
283 if(m_csRefName
.GetLength() > partialRefName
.GetLength())
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.
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
);
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))
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
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)
330 if(wcsncmp(ref
, L
"heads/", 6) == 0)
332 refs
+= ref
+ _T(" ");
338 //Tree ctrl selection?
339 HTREEITEM hTree
=m_RefTreeCtrl
.GetSelectedItem();
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
358 MAP_STRING_STRING
GetBranchDescriptions()
360 MAP_STRING_STRING descriptions
;
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
);
371 void CBrowseRefsDlg::Refresh(CString selectRef
)
374 // g_Git.GetMapHashToFriendName(m_RefMap);
376 if(!selectRef
.IsEmpty())
378 if(selectRef
== "HEAD")
380 selectRef
= g_Git
.GetSymbolicRef(selectRef
, false);
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="
399 L
"%(authordate:relative)%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
);
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");
418 CString refName
=singleRef
.Tokenize(L
"\04",valuePos
);
419 if(refName
.IsEmpty())
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
) )
427 if(wcsncmp(refName
,L
"refs/tags",9)==0 && !(m_pickRef_Kind
& gPickRef_Tag
) )
429 if(wcsncmp(refName
,L
"refs/remotes",12)==0 && !(m_pickRef_Kind
& gPickRef_Remote
) )
432 refMap
[refName
] = refRest
; //Use
435 MAP_STRING_STRING descriptions
= GetBranchDescriptions();
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)
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
)
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
);
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
);
501 CShadowTree
& CBrowseRefsDlg::GetTreeNode(CString refName
, CShadowTree
* pTreePos
, bool bCreateIfNotExist
)
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
);
515 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
516 ASSERT(!bCreateIfNotExist
);
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
);
541 FillListCtrlForTreeNode(pNMTreeView
->itemNew
.hItem
);
544 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode
)
546 m_ListRefLeafs
.DeleteAllItems();
548 CShadowTree
* pTree
=(CShadowTree
*)(m_RefTreeCtrl
.GetItemData(treeNode
));
554 FillListCtrlForShadowTree(pTree
,L
"",true);
557 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree
* pTree
, CString refNamePrefix
, bool isFirstLevel
)
562 m_ctrlFilter
.GetWindowText(filter
);
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
);
576 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Description
, pTree
->m_csDescription
.Tokenize(_T("\n"), pos
));
584 csThisName
=refNamePrefix
+pTree
->m_csRefName
+L
"/";
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);
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
)
606 msg
= msg
.MakeLower();
608 if (msg
.Find(filter
) >= 0)
612 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
614 CString msg
= pTree
->m_csSubject
;
615 msg
= msg
.MakeLower();
617 if (msg
.Find(filter
) >= 0)
621 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
623 CString msg
= pTree
->m_csAuthor
;
624 msg
= msg
.MakeLower();
626 if (msg
.Find(filter
) >= 0)
630 if (m_SelectedFilters
& LOGFILTER_REVS
)
632 CString msg
= pTree
->m_csRefHash
;
633 msg
= msg
.MakeLower();
635 if (msg
.Find(filter
) >= 0)
641 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree
& leafs
)
643 ASSERT(!leafs
.empty());
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;}
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
;
670 csMessage
+= L
"\r\n\r\n";
671 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
672 mbIcon
= MB_ICONWARNING
;
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
;
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
);
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
))
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;}
726 CString branchToDelete
= completeRefName
.Mid(bIsRemoteBranch
? 13 : 11);
730 CString remoteName
, remoteBranchToDelete
;
731 if (SplitRemoteBranchName(branchToDelete
, remoteName
, remoteBranchToDelete
))
734 if(CAppUtils::IsSSHPutty())
736 CAppUtils::LaunchPAgent(NULL
, &remoteName
);
739 cmd
.Format(L
"git.exe push \"%s\" :refs/heads/%s", remoteName
, remoteBranchToDelete
);
742 cmd
.Format(L
"git.exe branch -%c -- %s",bForce
?L
'D':L
'd',branchToDelete
);
743 CSysProgressDlg sysProgressDlg
;
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);
753 if(g_Git
.Run(cmd
,&errorMsg
,CP_UTF8
)!=0)
755 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
757 sysProgressDlg
.Stop();
761 sysProgressDlg
.Stop();
763 else if(wcsncmp(completeRefName
,L
"refs/tags",9)==0)
765 CString tagToDelete
= completeRefName
.Mid(10);
767 cmd
.Format(L
"git.exe tag -d -- %s",tagToDelete
);
769 if(g_Git
.Run(cmd
,&errorMsg
,CP_UTF8
)!=0)
771 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
778 CString
CBrowseRefsDlg::GetFullRefName(CString partialRefName
)
780 CShadowTree
* pLeaf
= m_TreeRoot
.FindLeaf(partialRefName
);
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
);
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();
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
);
829 return g_Git
.StripRefName(selectedLeafs
.at(0)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
832 void CBrowseRefsDlg::ShowContextMenu(CPoint point
, HTREEITEM hTreePos
, VectorPShadowTree
& selectedLeafs
)
835 popupMenu
.CreatePopupMenu();
837 bool bAddSeparator
= false;
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;
870 fetchFromCmd
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
872 else if(selectedLeafs
[0]->IsFrom(L
"refs/tags"))
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;
891 bAddSeparator
= true;
892 popupMenu
.AppendMenuIcon(eCmd_Fetch
, fetchFromCmd
, IDI_PULL
);
896 popupMenu
.AppendMenu(MF_SEPARATOR
);
898 bAddSeparator
= false;
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
);
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/"))
939 popupMenu
.AppendMenu(MF_SEPARATOR
);
940 CString menuItemName
;
941 if(selectedLeafs
.size() == 1)
942 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH
);
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/"))
952 popupMenu
.AppendMenu(MF_SEPARATOR
);
953 CString menuItemName
;
954 if(selectedLeafs
.size() == 1)
955 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH
);
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/"))
965 popupMenu
.AppendMenu(MF_SEPARATOR
);
966 CString menuItemName
;
967 if(selectedLeafs
.size() == 1)
968 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETETAG
);
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"))
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
))
992 if(!remoteName
.IsEmpty())
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"))
1006 popupMenu
.AppendMenu(MF_SEPARATOR
);
1008 temp
.LoadString(IDS_MENUBRANCH
);
1009 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
1011 if(pTree
->IsFrom(L
"refs/tags"))
1014 popupMenu
.AppendMenu(MF_SEPARATOR
);
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);
1030 dlg
.SetRange(g_Git
.FixBranchName(selectedLeafs
[0]->GetRefName()));
1034 case eCmd_ViewLogRange
:
1037 dlg
.SetRange(GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
1041 case eCmd_ViewLogRangeReachableFromOnlyOne
:
1044 dlg
.SetRange(GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
1048 case eCmd_RepoBrowser
:
1049 CAppUtils::RunTortoiseGitProc(_T("/command:repobrowser /path:\"") + g_Git
.m_CurrentDir
+ _T("\" /rev:") + selectedLeafs
[0]->GetRefName());
1051 case eCmd_DeleteBranch
:
1052 case eCmd_DeleteRemoteBranch
:
1054 if(ConfirmDeleteRef(selectedLeafs
))
1055 DoDeleteRefs(selectedLeafs
, true);
1059 case eCmd_DeleteTag
:
1061 if(ConfirmDeleteRef(selectedLeafs
))
1062 DoDeleteRefs(selectedLeafs
, true);
1066 case eCmd_ShowReflog
:
1068 CRefLogDlg
refLogDlg(this);
1069 refLogDlg
.m_CurrentBranch
= selectedLeafs
[0]->GetRefName();
1070 refLogDlg
.DoModal();
1075 CAppUtils::Fetch(remoteName
);
1079 case eCmd_DeleteRemoteTag
:
1081 CDeleteRemoteTagDlg deleteRemoteTagDlg
;
1082 deleteRemoteTagDlg
.m_sRemote
= remoteName
;
1083 deleteRemoteTagDlg
.DoModal();
1088 CAppUtils::Switch(selectedLeafs
[0]->GetRefName());
1093 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1095 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1098 case eCmd_AddRemote
:
1100 CAddRemoteDlg(this).DoModal();
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();
1112 case eCmd_CreateBranch
:
1114 CString
*commitHash
= NULL
;
1115 if (selectedLeafs
.size() == 1)
1116 commitHash
= &(selectedLeafs
[0]->m_csRefHash
);
1117 CAppUtils::CreateBranchTag(false, commitHash
);
1121 case eCmd_CreateTag
:
1123 CAppUtils::CreateBranchTag(true);
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);
1144 selectedLeafs
[0]->m_csRefHash
,
1145 selectedLeafs
[1]->m_csRefHash
);
1149 case eCmd_EditBranchDescription
:
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
)
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
);
1165 g_Git
.SetConfigValue(key
, dlg
.m_sInputText
);
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
))
1181 BOOL
CBrowseRefsDlg::PreTranslateMessage(MSG
* pMsg
)
1183 if (pMsg
->message
== WM_KEYDOWN
)
1185 switch (pMsg
->wParam
)
1189 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1191 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1193 PostMessage(WM_COMMAND, IDOK);
1201 if(pMsg
->hwnd
== m_ListRefLeafs
.m_hWnd
)
1203 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1205 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1219 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
1222 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1224 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1227 if(m_currSortCol
== pNMLV
->iSubItem
)
1228 m_currSortDesc
= !m_currSortDesc
;
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
);
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
)
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
)
1278 return dlg
.m_pickedRef
;
1281 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx
* pComboBox
, int pickRef_Kind
)
1284 pComboBox
->GetLBText(pComboBox
->GetCurSel(), origRef
);
1285 CString resultRef
= PickRef(false,origRef
,pickRef_Kind
);
1286 if(resultRef
.IsEmpty())
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
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();
1309 pComboBox
->SetCurSel(ixFound
);
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)
1316 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1318 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
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
);
1332 CString origName
= pTree
->GetRefName().Mid(11);
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
);
1345 CString newNameTrunced
= newName
.Mid(11);
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
);
1353 //Do as if it failed to rename. Let Refresh() do the job.
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
);
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.
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.
1400 RECT
* rect
= (LPRECT
)lParam
;
1403 point
= CPoint(rect
->left
, rect
->bottom
);
1404 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
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);
1423 m_SelectedFilters
^= selection
;
1425 SetTimer(IDT_FILTER
, 1000, NULL
);
1431 void CBrowseRefsDlg::SetFilterCueText()
1433 CString
temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY
));
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)
1443 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT
));
1446 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
1448 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1450 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS
));
1453 if (m_SelectedFilters
& LOGFILTER_REVS
)
1455 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
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());