1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2014 - 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(const CShadowTree
* pLeft
, const CShadowTree
* pRight
)
115 int result
=CompareNoDesc(pLeft
,pRight
);
121 int CompareNoDesc(const CShadowTree
* pLeft
, const CShadowTree
* pRight
)
125 case CBrowseRefsDlg::eCol_Name
: return SortStrCmp(pLeft
->GetRefName(), pRight
->GetRefName());
126 case CBrowseRefsDlg::eCol_Upstream
: return SortStrCmp(pLeft
->m_csUpstream
, pRight
->m_csUpstream
);
127 case CBrowseRefsDlg::eCol_Date
: return pLeft
->m_csDate_Iso8601
.CompareNoCase(pRight
->m_csDate_Iso8601
);
128 case CBrowseRefsDlg::eCol_Msg
: return SortStrCmp(pLeft
->m_csSubject
, pRight
->m_csSubject
);
129 case CBrowseRefsDlg::eCol_LastAuthor
: return SortStrCmp(pLeft
->m_csAuthor
, pRight
->m_csAuthor
);
130 case CBrowseRefsDlg::eCol_Hash
: return pLeft
->m_csRefHash
.CompareNoCase(pRight
->m_csRefHash
);
131 case CBrowseRefsDlg::eCol_Description
: return SortStrCmp(pLeft
->m_csDescription
, pRight
->m_csDescription
);
135 int SortStrCmp(const CString
& left
, const CString
& right
)
138 return StrCmpLogicalW(left
, right
);
139 return StrCmpI(left
, right
);
148 // CBrowseRefsDlg dialog
150 IMPLEMENT_DYNAMIC(CBrowseRefsDlg
, CResizableStandAloneDialog
)
152 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath
, CWnd
* pParent
/*=NULL*/)
153 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD
, pParent
),
156 m_currSortDesc(false),
157 m_initialRef(L
"HEAD"),
158 m_pickRef_Kind(gPickRef_All
),
159 m_pListCtrlRoot(NULL
),
161 m_SelectedFilters(LOGFILTER_ALL
),
162 m_ColumnManager(&m_ListRefLeafs
),
164 m_bPickedRefSet(false)
169 CBrowseRefsDlg::~CBrowseRefsDlg()
173 void CBrowseRefsDlg::DoDataExchange(CDataExchange
* pDX
)
175 CDialog::DoDataExchange(pDX
);
176 DDX_Control(pDX
, IDC_TREE_REF
, m_RefTreeCtrl
);
177 DDX_Control(pDX
, IDC_LIST_REF_LEAFS
, m_ListRefLeafs
);
178 DDX_Control(pDX
, IDC_BROWSEREFS_EDIT_FILTER
, m_ctrlFilter
);
182 BEGIN_MESSAGE_MAP(CBrowseRefsDlg
, CResizableStandAloneDialog
)
183 ON_BN_CLICKED(IDOK
, &CBrowseRefsDlg::OnBnClickedOk
)
184 ON_NOTIFY(TVN_SELCHANGED
, IDC_TREE_REF
, &CBrowseRefsDlg::OnTvnSelchangedTreeRef
)
186 ON_NOTIFY(LVN_COLUMNCLICK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs
)
188 ON_NOTIFY(NM_DBLCLK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnNMDblclkListRefLeafs
)
189 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnItemChangedListRefLeafs
)
190 ON_NOTIFY(LVN_ENDLABELEDIT
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs
)
191 ON_NOTIFY(LVN_BEGINLABELEDIT
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs
)
192 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER
, &CBrowseRefsDlg::OnEnChangeEditFilter
)
193 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED
, OnClickedInfoIcon
)
195 ON_BN_CLICKED(IDC_CURRENTBRANCH
, OnBnClickedCurrentbranch
)
199 // CBrowseRefsDlg message handlers
201 void CBrowseRefsDlg::OnBnClickedOk()
203 if (m_bPickOne
|| m_ListRefLeafs
.GetSelectedCount() != 2)
210 popupMenu
.CreatePopupMenu();
212 std::vector
<CShadowTree
*> selectedLeafs
;
213 GetSelectedLeaves(selectedLeafs
);
215 popupMenu
.AppendMenuIcon(1, GetSelectedRef(true, false), IDI_LOG
);
216 popupMenu
.SetDefaultItem(1);
217 popupMenu
.AppendMenuIcon(2, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")), IDI_LOG
);
218 popupMenu
.AppendMenuIcon(3, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")), IDI_LOG
);
221 GetDlgItem(IDOK
)->GetWindowRect(&rect
);
222 int selection
= popupMenu
.TrackPopupMenuEx(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, rect
.left
, rect
.top
, this, 0);
230 m_bPickedRefSet
= true;
231 m_pickedRef
= GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T(".."));
237 m_bPickedRefSet
= true;
238 m_pickedRef
= GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..."));
247 BOOL
CBrowseRefsDlg::OnInitDialog()
249 CResizableStandAloneDialog::OnInitDialog();
250 CAppUtils::MarkWindowAsUnpinnable(m_hWnd
);
252 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
253 m_ctrlFilter
.SetCancelBitmaps(IDI_CANCELNORMAL
, IDI_CANCELPRESSED
);
254 m_ctrlFilter
.SetInfoIcon(IDI_FILTEREDIT
);
257 AddAnchor(IDC_TREE_REF
, TOP_LEFT
, BOTTOM_LEFT
);
258 AddAnchor(IDC_LIST_REF_LEAFS
, TOP_LEFT
, BOTTOM_RIGHT
);
259 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER
, TOP_LEFT
);
260 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER
, TOP_LEFT
, TOP_RIGHT
);
261 AddAnchor(IDC_INFOLABEL
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
262 AddAnchor(IDHELP
, BOTTOM_RIGHT
);
264 m_ListRefLeafs
.SetExtendedStyle(m_ListRefLeafs
.GetExtendedStyle()|LVS_EX_FULLROWSELECT
);
265 static UINT columnNames
[] = { IDS_BRANCHNAME
, IDS_TRACKEDBRANCH
, IDS_DATELASTCOMMIT
, IDS_LASTCOMMIT
, IDS_LASTAUTHOR
, IDS_HASH
, IDS_DESCRIPTION
};
266 static int columnWidths
[] = { 150, 100, 100, 300, 100, 80, 80 };
267 DWORD dwDefaultColumns
= (1 << eCol_Name
) | (1 << eCol_Upstream
) | (1 << eCol_Date
) | (1 << eCol_Msg
) |
268 (1 << eCol_LastAuthor
) | (1 << eCol_Hash
) | (1 << eCol_Description
);
269 m_ColumnManager
.SetNames(columnNames
, _countof(columnNames
));
270 m_ColumnManager
.ReadSettings(dwDefaultColumns
, 0, _T("BrowseRefs"), _countof(columnNames
), columnWidths
);
271 m_bPickedRefSet
= false;
273 AddAnchor(IDOK
,BOTTOM_RIGHT
);
274 AddAnchor(IDCANCEL
,BOTTOM_RIGHT
);
275 AddAnchor(IDC_CURRENTBRANCH
, BOTTOM_RIGHT
);
277 Refresh(m_initialRef
);
279 EnableSaveRestore(L
"BrowseRefs");
281 CString sWindowTitle
;
282 GetWindowText(sWindowTitle
);
283 CAppUtils::SetWindowTitle(m_hWnd
, g_Git
.m_CurrentDir
, sWindowTitle
);
285 m_bHasWC
= !g_GitAdminDir
.IsBareRepo(g_Git
.m_CurrentDir
);
288 m_ListRefLeafs
.ModifyStyle(0, LVS_SINGLESEL
);
290 m_ListRefLeafs
.SetFocus();
294 CShadowTree
* CShadowTree::GetNextSub(CString
& nameLeft
, bool bCreateIfNotExist
)
296 int posSlash
=nameLeft
.Find('/');
301 nameLeft
.Empty();//Nothing left
305 nameSub
=nameLeft
.Left(posSlash
);
306 nameLeft
=nameLeft
.Mid(posSlash
+1);
308 if(nameSub
.IsEmpty())
311 if(!bCreateIfNotExist
&& m_ShadowTree
.find(nameSub
)==m_ShadowTree
.end())
314 CShadowTree
& nextNode
=m_ShadowTree
[nameSub
];
315 nextNode
.m_csRefName
=nameSub
;
316 nextNode
.m_pParent
=this;
320 CShadowTree
* CShadowTree::FindLeaf(CString partialRefName
)
324 if(m_csRefName
.GetLength() > partialRefName
.GetLength())
326 if(partialRefName
.Right(m_csRefName
.GetLength()) == m_csRefName
)
328 //Match of leaf name. Try match on total name.
329 CString totalRefName
= GetRefName();
330 if(totalRefName
.Right(partialRefName
.GetLength()) == partialRefName
)
331 return this; //Also match. Found.
336 //Not a leaf. Search all nodes.
337 for(TShadowTreeMap::iterator itShadowTree
= m_ShadowTree
.begin(); itShadowTree
!= m_ShadowTree
.end(); ++itShadowTree
)
339 CShadowTree
* pSubtree
= itShadowTree
->second
.FindLeaf(partialRefName
);
341 return pSubtree
; //Found
344 return NULL
;//Not found
348 typedef std::map
<CString
,CString
> MAP_STRING_STRING
;
350 CString
CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf
, bool pickFirstSelIfMultiSel
)
352 POSITION pos
=m_ListRefLeafs
.GetFirstSelectedItemPosition();
353 //List ctrl selection?
354 if(pos
&& (pickFirstSelIfMultiSel
|| m_ListRefLeafs
.GetSelectedCount() == 1))
357 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(
358 m_ListRefLeafs
.GetNextSelectedItem(pos
));
359 return pTree
->GetRefName();
361 else if (pos
&& !pickFirstSelIfMultiSel
)
363 // at least one leaf is selected
366 while ((index
= m_ListRefLeafs
.GetNextSelectedItem(pos
)) >= 0)
368 CString ref
= ((CShadowTree
*)m_ListRefLeafs
.GetItemData(index
))->GetRefName();
369 if(wcsncmp(ref
, L
"refs/", 5) == 0)
371 if(wcsncmp(ref
, L
"heads/", 6) == 0)
373 refs
+= ref
+ _T(" ");
379 //Tree ctrl selection?
380 HTREEITEM hTree
=m_RefTreeCtrl
.GetSelectedItem();
383 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTree
);
384 return pTree
->GetRefName();
387 return CString();//None
390 static int GetBranchDescriptionsCallback(const git_config_entry
*entry
, void *data
)
392 MAP_STRING_STRING
*descriptions
= (MAP_STRING_STRING
*) data
;
393 CString key
= CUnicodeUtils::GetUnicode(entry
->name
, CP_UTF8
);
394 CString val
= CUnicodeUtils::GetUnicode(entry
->value
, CP_UTF8
);
395 descriptions
->insert(std::make_pair(key
.Mid(7, key
.GetLength() - 7 - 12), val
)); // 7: branch., 12: .description
399 MAP_STRING_STRING
GetBranchDescriptions()
401 MAP_STRING_STRING descriptions
;
403 git_config_new(&config
);
404 git_config_add_file_ondisk(config
, CGit::GetGitPathStringA(g_Git
.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL
, FALSE
);
405 git_config_foreach_match(config
, "branch\\..*\\.description", GetBranchDescriptionsCallback
, &descriptions
);
406 git_config_free(config
);
410 void CBrowseRefsDlg::Refresh(CString selectRef
)
413 // g_Git.GetMapHashToFriendName(m_RefMap);
415 if(!selectRef
.IsEmpty())
417 if(selectRef
== "HEAD")
419 selectRef
= g_Git
.GetSymbolicRef(selectRef
, false);
424 selectRef
= GetSelectedRef(false, true);
427 m_RefTreeCtrl
.DeleteAllItems();
428 m_ListRefLeafs
.DeleteAllItems();
429 m_TreeRoot
.m_ShadowTree
.clear();
430 m_TreeRoot
.m_csRefName
= "refs";
431 m_TreeRoot
.m_hTree
=m_RefTreeCtrl
.InsertItem(L
"refs",NULL
,NULL
);
432 m_RefTreeCtrl
.SetItemData(m_TreeRoot
.m_hTree
,(DWORD_PTR
)&m_TreeRoot
);
434 CString allRefs
, error
;
435 if (g_Git
.Run(L
"git.exe for-each-ref --format="
439 L
"%(authordate:relative)%04"
442 L
"%(authordate:iso8601)%03",
443 &allRefs
, &error
, CP_UTF8
))
445 CMessageBox::Show(NULL
, CString(_T("Get refs failed\n")) + error
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
451 MAP_STRING_STRING refMap
;
453 //First sort on ref name
454 while(!(singleRef
=allRefs
.Tokenize(L
"\03",linePos
)).IsEmpty())
456 singleRef
.TrimLeft(L
"\r\n");
458 CString refName
=singleRef
.Tokenize(L
"\04",valuePos
);
459 if(refName
.IsEmpty())
461 CString refRest
=singleRef
.Mid(valuePos
);
464 //Use ref based on m_pickRef_Kind
465 if (wcsncmp(refName
, L
"refs/heads/", 11) == 0 && !(m_pickRef_Kind
& gPickRef_Head
))
467 if (wcsncmp(refName
, L
"refs/tags/", 10) == 0 && !(m_pickRef_Kind
& gPickRef_Tag
))
469 if (wcsncmp(refName
, L
"refs/remotes/", 13) == 0 && !(m_pickRef_Kind
& gPickRef_Remote
))
472 refMap
[refName
] = refRest
; //Use
475 MAP_STRING_STRING descriptions
= GetBranchDescriptions();
478 for(MAP_STRING_STRING::iterator iterRefMap
=refMap
.begin();iterRefMap
!=refMap
.end();++iterRefMap
)
480 CShadowTree
& treeLeaf
=GetTreeNode(iterRefMap
->first
,NULL
,true);
481 CString values
=iterRefMap
->second
;
482 values
.Replace(L
"\04" L
"\04",L
"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
485 treeLeaf
.m_csRefHash
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
486 treeLeaf
.m_csUpstream
= values
.Tokenize(L
"\04", valuePos
); if (valuePos
< 0) continue;
487 CGit::GetShortName(treeLeaf
.m_csUpstream
, treeLeaf
.m_csUpstream
, L
"refs/remotes/");
488 treeLeaf
.m_csDate
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
489 treeLeaf
.m_csSubject
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
490 treeLeaf
.m_csAuthor
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
491 treeLeaf
.m_csDate_Iso8601
= values
.Tokenize(L
"\04",valuePos
);
493 if (wcsncmp(iterRefMap
->first
, L
"refs/heads/", 11) == 0)
494 treeLeaf
.m_csDescription
= descriptions
[treeLeaf
.m_csRefName
];
497 // always expand the tree first
498 m_RefTreeCtrl
.Expand(m_TreeRoot
.m_hTree
, TVE_EXPAND
);
500 // try exact match first
501 if (!selectRef
.IsEmpty() && !SelectRef(selectRef
, true))
502 SelectRef(selectRef
, false);
505 bool CBrowseRefsDlg::SelectRef(CString refName
, bool bExactMatch
)
509 CString newRefName
= GetFullRefName(refName
);
510 if(!newRefName
.IsEmpty())
511 refName
= newRefName
;
512 //else refName is not a valid ref. Try to select as good as possible.
514 if(_wcsnicmp(refName
, L
"refs/", 5) != 0)
515 return false; // Not a ref name
517 CShadowTree
& treeLeafHead
=GetTreeNode(refName
,NULL
,false);
518 if(treeLeafHead
.m_hTree
!= NULL
)
520 //Not a leaf. Select tree node and return
521 m_RefTreeCtrl
.Select(treeLeafHead
.m_hTree
,TVGN_CARET
);
525 if(treeLeafHead
.m_pParent
==NULL
)
526 return false; //Weird... should not occur.
528 //This is the current head.
529 m_RefTreeCtrl
.Select(treeLeafHead
.m_pParent
->m_hTree
,TVGN_CARET
);
531 for(int indexPos
= 0; indexPos
< m_ListRefLeafs
.GetItemCount(); ++indexPos
)
533 CShadowTree
* pCurrShadowTree
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(indexPos
);
534 if(pCurrShadowTree
== &treeLeafHead
)
536 m_ListRefLeafs
.SetItemState(indexPos
,LVIS_SELECTED
,LVIS_SELECTED
);
537 m_ListRefLeafs
.EnsureVisible(indexPos
,FALSE
);
544 CShadowTree
& CBrowseRefsDlg::GetTreeNode(CString refName
, CShadowTree
* pTreePos
, bool bCreateIfNotExist
)
548 if(_wcsnicmp(refName
, L
"refs/", 5) == 0)
549 refName
=refName
.Mid(5);
550 pTreePos
=&m_TreeRoot
;
552 if(refName
.IsEmpty())
553 return *pTreePos
;//Found leaf
555 CShadowTree
* pNextTree
=pTreePos
->GetNextSub(refName
,bCreateIfNotExist
);
558 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
559 ASSERT(!bCreateIfNotExist
);
563 if(!refName
.IsEmpty())
565 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
566 //Leafs are for the list control.
567 if(pNextTree
->m_hTree
==NULL
)
569 //New tree. Create node in control.
570 pNextTree
->m_hTree
=m_RefTreeCtrl
.InsertItem(pNextTree
->m_csRefName
,pTreePos
->m_hTree
,NULL
);
571 m_RefTreeCtrl
.SetItemData(pNextTree
->m_hTree
,(DWORD_PTR
)pNextTree
);
575 return GetTreeNode(refName
, pNextTree
, bCreateIfNotExist
);
579 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR
*pNMHDR
, LRESULT
*pResult
)
581 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
584 FillListCtrlForTreeNode(pNMTreeView
->itemNew
.hItem
);
587 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode
)
589 m_ListRefLeafs
.DeleteAllItems();
591 CShadowTree
* pTree
=(CShadowTree
*)(m_RefTreeCtrl
.GetItemData(treeNode
));
597 FillListCtrlForShadowTree(pTree
,L
"",true);
598 m_ColumnManager
.SetVisible(eCol_Upstream
, (wcsncmp(pTree
->GetRefName(), L
"refs/heads", 11) == 0));
601 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree
* pTree
, CString refNamePrefix
, bool isFirstLevel
)
606 m_ctrlFilter
.GetWindowText(filter
);
608 CString ref
= refNamePrefix
+ pTree
->m_csRefName
;
609 if (!(pTree
->m_csRefName
.IsEmpty() || pTree
->m_csRefName
== "refs" && pTree
->m_pParent
== NULL
) && IsMatchFilter(pTree
, ref
, filter
))
611 int indexItem
= m_ListRefLeafs
.InsertItem(m_ListRefLeafs
.GetItemCount(), L
"");
613 m_ListRefLeafs
.SetItemData(indexItem
,(DWORD_PTR
)pTree
);
614 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Name
, ref
);
615 m_ListRefLeafs
.SetItemText(indexItem
, eCol_Upstream
, pTree
->m_csUpstream
);
616 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Date
, pTree
->m_csDate
);
617 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Msg
, pTree
->m_csSubject
);
618 m_ListRefLeafs
.SetItemText(indexItem
,eCol_LastAuthor
, pTree
->m_csAuthor
);
619 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Hash
, pTree
->m_csRefHash
);
621 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Description
, pTree
->m_csDescription
.Tokenize(_T("\n"), pos
));
629 csThisName
=refNamePrefix
+pTree
->m_csRefName
+L
"/";
631 m_pListCtrlRoot
= pTree
;
632 for(CShadowTree::TShadowTreeMap::iterator itSubTree
=pTree
->m_ShadowTree
.begin(); itSubTree
!=pTree
->m_ShadowTree
.end(); ++itSubTree
)
634 FillListCtrlForShadowTree(&itSubTree
->second
,csThisName
,false);
639 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
640 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
642 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
646 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree
* pTree
, const CString
&ref
, const CString
&filter
)
648 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
651 msg
= msg
.MakeLower();
653 if (msg
.Find(filter
) >= 0)
657 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
659 CString msg
= pTree
->m_csSubject
;
660 msg
= msg
.MakeLower();
662 if (msg
.Find(filter
) >= 0)
666 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
668 CString msg
= pTree
->m_csAuthor
;
669 msg
= msg
.MakeLower();
671 if (msg
.Find(filter
) >= 0)
675 if (m_SelectedFilters
& LOGFILTER_REVS
)
677 CString msg
= pTree
->m_csRefHash
;
678 msg
= msg
.MakeLower();
680 if (msg
.Find(filter
) >= 0)
686 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree
& leafs
)
688 ASSERT(!leafs
.empty());
691 UINT mbIcon
=MB_ICONQUESTION
;
693 bool bIsRemoteBranch
= false;
694 bool bIsBranch
= false;
695 if (leafs
[0]->IsFrom(L
"refs/remotes/")) {bIsBranch
= true; bIsRemoteBranch
= true;}
696 else if (leafs
[0]->IsFrom(L
"refs/heads/")) {bIsBranch
= true;}
700 if(leafs
.size() == 1)
702 CString branchToDelete
= leafs
[0]->GetRefName().Mid(bIsRemoteBranch
? 13 : 11);
703 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, branchToDelete
);
705 //Check if branch is fully merged in HEAD
706 if (!g_Git
.IsFastForward(leafs
[0]->GetRefName(), _T("HEAD")))
708 csMessage
+= L
"\r\n\r\n";
709 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED
));
710 mbIcon
= MB_ICONWARNING
;
715 csMessage
+= L
"\r\n\r\n";
716 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
717 mbIcon
= MB_ICONWARNING
;
722 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
724 csMessage
+= L
"\r\n\r\n";
725 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK
));
726 mbIcon
= MB_ICONWARNING
;
730 csMessage
+= L
"\r\n\r\n";
731 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
732 mbIcon
= MB_ICONWARNING
;
737 else if(leafs
[0]->IsFrom(L
"refs/tags/"))
739 if(leafs
.size() == 1)
741 CString tagToDelete
= leafs
[0]->GetRefName().Mid(10);
742 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, tagToDelete
);
746 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
750 return CMessageBox::Show(m_hWnd
, csMessage
, _T("TortoiseGit"), MB_YESNO
| mbIcon
) == IDYES
;
754 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree
& leafs
)
756 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
757 if(!DoDeleteRef((*i
)->GetRefName()))
762 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName
)
764 bool bIsRemoteBranch
= false;
765 bool bIsBranch
= false;
766 if (wcsncmp(completeRefName
, L
"refs/remotes/",13) == 0) {bIsBranch
= true; bIsRemoteBranch
= true;}
767 else if (wcsncmp(completeRefName
, L
"refs/heads/",11) == 0) {bIsBranch
= true;}
771 CString branchToDelete
= completeRefName
.Mid(13);
773 CString remoteName
, remoteBranchToDelete
;
774 if (SplitRemoteBranchName(branchToDelete
, remoteName
, remoteBranchToDelete
))
777 if (CAppUtils::IsSSHPutty())
778 CAppUtils::LaunchPAgent(NULL
, &remoteName
);
780 cmd
.Format(L
"git.exe push \"%s\" :refs/heads/%s", remoteName
, remoteBranchToDelete
);
782 CSysProgressDlg sysProgressDlg
;
783 sysProgressDlg
.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME
)));
784 sysProgressDlg
.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS
)));
785 sysProgressDlg
.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT
)));
786 sysProgressDlg
.SetShowProgressBar(false);
787 sysProgressDlg
.ShowModal(this, true);
790 if (g_Git
.Run(cmd
, &errorMsg
, CP_UTF8
) != 0)
792 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
793 sysProgressDlg
.Stop();
797 sysProgressDlg
.Stop();
802 if (g_Git
.DeleteRef(completeRefName
))
804 CMessageBox::Show(m_hWnd
, g_Git
.GetGitLastErr(L
"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH
), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
808 else if (wcsncmp(completeRefName
, L
"refs/tags/", 10) == 0)
810 if (g_Git
.DeleteRef(completeRefName
))
812 CMessageBox::Show(m_hWnd
, g_Git
.GetGitLastErr(L
"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH
), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
819 CString
CBrowseRefsDlg::GetFullRefName(CString partialRefName
)
821 CShadowTree
* pLeaf
= m_TreeRoot
.FindLeaf(partialRefName
);
824 return pLeaf
->GetRefName();
828 void CBrowseRefsDlg::OnContextMenu(CWnd
* pWndFrom
, CPoint point
)
830 if(pWndFrom
==&m_RefTreeCtrl
) OnContextMenu_RefTreeCtrl(point
);
831 else if(pWndFrom
==&m_ListRefLeafs
) OnContextMenu_ListRefLeafs(point
);
834 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point
)
836 CPoint clientPoint
=point
;
837 m_RefTreeCtrl
.ScreenToClient(&clientPoint
);
839 HTREEITEM hTreeItem
=m_RefTreeCtrl
.HitTest(clientPoint
);
841 m_RefTreeCtrl
.Select(hTreeItem
,TVGN_CARET
);
843 VectorPShadowTree tree
;
844 ShowContextMenu(point
,hTreeItem
,tree
);
847 void CBrowseRefsDlg::GetSelectedLeaves(VectorPShadowTree
& selectedLeafs
)
849 selectedLeafs
.reserve(m_ListRefLeafs
.GetSelectedCount());
850 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
853 selectedLeafs
.push_back((CShadowTree
*)m_ListRefLeafs
.GetItemData(m_ListRefLeafs
.GetNextSelectedItem(pos
)));
857 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point
)
859 std::vector
<CShadowTree
*> selectedLeafs
;
860 GetSelectedLeaves(selectedLeafs
);
861 ShowContextMenu(point
,m_RefTreeCtrl
.GetSelectedItem(),selectedLeafs
);
864 CString
CBrowseRefsDlg::GetTwoSelectedRefs(VectorPShadowTree
& selectedLeafs
, const CString
&lastSelected
, const CString
&separator
)
866 ASSERT(selectedLeafs
.size() == 2);
868 if (selectedLeafs
.at(0)->GetRefName() == lastSelected
)
869 return g_Git
.StripRefName(selectedLeafs
.at(1)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
871 return g_Git
.StripRefName(selectedLeafs
.at(0)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
874 void CBrowseRefsDlg::ShowContextMenu(CPoint point
, HTREEITEM hTreePos
, VectorPShadowTree
& selectedLeafs
)
877 popupMenu
.CreatePopupMenu();
879 bool bAddSeparator
= false;
882 if(selectedLeafs
.size()==1)
884 bAddSeparator
= true;
886 bool bShowReflogOption
= false;
887 bool bShowFetchOption
= false;
888 bool bShowRenameOption
= false;
889 bool bShowCreateBranchOption
= false;
890 bool bShowEditBranchDescriptionOption
= false;
892 CString fetchFromCmd
;
894 if(selectedLeafs
[0]->IsFrom(L
"refs/heads/"))
896 bShowReflogOption
= true;
897 bShowRenameOption
= true;
898 bShowEditBranchDescriptionOption
= true;
900 else if(selectedLeafs
[0]->IsFrom(L
"refs/remotes/"))
902 bShowReflogOption
= true;
903 bShowFetchOption
= true;
904 bShowCreateBranchOption
= true;
906 CString remoteBranch
;
907 if (SplitRemoteBranchName(selectedLeafs
[0]->GetRefName(), remoteName
, remoteBranch
))
908 bShowFetchOption
= false;
910 fetchFromCmd
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
912 else if(selectedLeafs
[0]->IsFrom(L
"refs/tags/"))
917 temp
.LoadString(IDS_MENULOG
);
918 popupMenu
.AppendMenuIcon(eCmd_ViewLog
, temp
, IDI_LOG
);
919 popupMenu
.AppendMenuIcon(eCmd_RepoBrowser
, IDS_LOG_BROWSEREPO
, IDI_REPOBROWSE
);
920 if(bShowReflogOption
)
922 temp
.LoadString(IDS_MENUREFLOG
);
923 popupMenu
.AppendMenuIcon(eCmd_ShowReflog
, temp
, IDI_LOG
);
926 popupMenu
.AppendMenu(MF_SEPARATOR
);
927 bAddSeparator
= false;
931 bAddSeparator
= true;
932 popupMenu
.AppendMenuIcon(eCmd_Fetch
, fetchFromCmd
, IDI_PULL
);
936 popupMenu
.AppendMenu(MF_SEPARATOR
);
938 bAddSeparator
= false;
942 if (selectedLeafs
[0]->GetRefName() != _T("refs/heads/") + g_Git
.GetCurrentBranch())
944 format
.LoadString(IDS_LOG_POPUP_MERGEREV
);
945 str
.Format(format
, g_Git
.GetCurrentBranch());
946 popupMenu
.AppendMenuIcon(eCmd_Merge
, str
, IDI_MERGE
);
948 popupMenu
.AppendMenuIcon(eCmd_Switch
, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS
)), IDI_SWITCH
);
949 popupMenu
.AppendMenu(MF_SEPARATOR
);
952 if(bShowCreateBranchOption
)
954 bAddSeparator
= true;
955 temp
.LoadString(IDS_MENUBRANCH
);
956 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
959 if (bShowEditBranchDescriptionOption
)
961 bAddSeparator
= true;
962 popupMenu
.AppendMenuIcon(eCmd_EditBranchDescription
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
)), IDI_RENAME
);
964 if(bShowRenameOption
)
966 bAddSeparator
= true;
967 popupMenu
.AppendMenuIcon(eCmd_Rename
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME
)), IDI_RENAME
);
970 else if(selectedLeafs
.size() == 2)
972 bAddSeparator
= true;
973 popupMenu
.AppendMenuIcon(eCmd_Diff
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS
)), IDI_DIFF
);
974 popupMenu
.AppendMenuIcon(eCmd_UnifiedDiff
, CString(MAKEINTRESOURCE(IDS_LOG_POPUP_GNUDIFF
)), IDI_DIFF
);
976 menu
.Format(IDS_SHOWLOG_OF
, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
977 popupMenu
.AppendMenuIcon(eCmd_ViewLogRange
, menu
, IDI_LOG
);
978 menu
.Format(IDS_SHOWLOG_OF
, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
979 popupMenu
.AppendMenuIcon(eCmd_ViewLogRangeReachableFromOnlyOne
, menu
, IDI_LOG
);
982 if(!selectedLeafs
.empty())
984 if(AreAllFrom(selectedLeafs
, L
"refs/remotes/"))
987 popupMenu
.AppendMenu(MF_SEPARATOR
);
988 CString menuItemName
;
989 if(selectedLeafs
.size() == 1)
990 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH
);
992 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES
, selectedLeafs
.size());
994 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteBranch
, menuItemName
, IDI_DELETE
);
995 bAddSeparator
= true;
997 else if(AreAllFrom(selectedLeafs
, L
"refs/heads/"))
1000 popupMenu
.AppendMenu(MF_SEPARATOR
);
1001 CString menuItemName
;
1002 if(selectedLeafs
.size() == 1)
1003 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH
);
1005 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES
, selectedLeafs
.size());
1007 popupMenu
.AppendMenuIcon(eCmd_DeleteBranch
, menuItemName
, IDI_DELETE
);
1008 bAddSeparator
= true;
1010 else if(AreAllFrom(selectedLeafs
, L
"refs/tags/"))
1013 popupMenu
.AppendMenu(MF_SEPARATOR
);
1014 CString menuItemName
;
1015 if(selectedLeafs
.size() == 1)
1016 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETETAG
);
1018 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETETAGS
, selectedLeafs
.size());
1020 popupMenu
.AppendMenuIcon(eCmd_DeleteTag
, menuItemName
, IDI_DELETE
);
1021 bAddSeparator
= true;
1026 if(hTreePos
!=NULL
&& selectedLeafs
.empty())
1028 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTreePos
);
1029 if(pTree
->IsFrom(L
"refs/remotes"))
1032 popupMenu
.AppendMenu(MF_SEPARATOR
);
1033 popupMenu
.AppendMenuIcon(eCmd_ManageRemotes
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES
)), IDI_SETTINGS
);
1034 bAddSeparator
= true;
1035 if(selectedLeafs
.empty())
1037 CString remoteBranch
;
1038 if (SplitRemoteBranchName(pTree
->GetRefName(), remoteName
, remoteBranch
))
1039 remoteName
= _T("");
1040 if(!remoteName
.IsEmpty())
1043 temp
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
1044 popupMenu
.AppendMenuIcon(eCmd_Fetch
, temp
, IDI_PULL
);
1046 temp
.LoadString(IDS_DELETEREMOTETAG
);
1047 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteTag
, temp
, IDI_DELETE
);
1051 if(pTree
->IsFrom(L
"refs/heads"))
1054 popupMenu
.AppendMenu(MF_SEPARATOR
);
1056 temp
.LoadString(IDS_MENUBRANCH
);
1057 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
1059 if(pTree
->IsFrom(L
"refs/tags"))
1062 popupMenu
.AppendMenu(MF_SEPARATOR
);
1064 temp
.LoadString(IDS_MENUTAG
);
1065 popupMenu
.AppendMenuIcon(eCmd_CreateTag
, temp
, IDI_TAG
);
1066 temp
.LoadString(IDS_PROC_BROWSEREFS_DELETEALLTAGS
);
1067 popupMenu
.AppendMenuIcon(eCmd_DeleteAllTags
, temp
, IDI_DELETE
);
1072 eCmd cmd
=(eCmd
)popupMenu
.TrackPopupMenuEx(TPM_LEFTALIGN
|TPM_RETURNCMD
, point
.x
, point
.y
, this, 0);
1078 dlg
.SetRange(g_Git
.FixBranchName(selectedLeafs
[0]->GetRefName()));
1082 case eCmd_ViewLogRange
:
1085 dlg
.SetRange(GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
1089 case eCmd_ViewLogRangeReachableFromOnlyOne
:
1092 dlg
.SetRange(GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
1096 case eCmd_RepoBrowser
:
1097 CAppUtils::RunTortoiseGitProc(_T("/command:repobrowser /path:\"") + g_Git
.m_CurrentDir
+ _T("\" /rev:") + selectedLeafs
[0]->GetRefName());
1099 case eCmd_DeleteBranch
:
1100 case eCmd_DeleteRemoteBranch
:
1102 if(ConfirmDeleteRef(selectedLeafs
))
1103 DoDeleteRefs(selectedLeafs
);
1107 case eCmd_DeleteTag
:
1109 if(ConfirmDeleteRef(selectedLeafs
))
1110 DoDeleteRefs(selectedLeafs
);
1114 case eCmd_ShowReflog
:
1116 CRefLogDlg
refLogDlg(this);
1117 refLogDlg
.m_CurrentBranch
= selectedLeafs
[0]->GetRefName();
1118 refLogDlg
.DoModal();
1123 CAppUtils::Fetch(remoteName
);
1127 case eCmd_DeleteRemoteTag
:
1129 CDeleteRemoteTagDlg deleteRemoteTagDlg
;
1130 deleteRemoteTagDlg
.m_sRemote
= remoteName
;
1131 deleteRemoteTagDlg
.DoModal();
1136 CString ref
= selectedLeafs
[0]->GetRefName();
1137 CAppUtils::Merge(&ref
);
1142 CAppUtils::Switch(selectedLeafs
[0]->GetRefName());
1147 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1149 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1152 case eCmd_AddRemote
:
1154 CAddRemoteDlg(this).DoModal();
1158 case eCmd_ManageRemotes
:
1160 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS
)), new CSettingGitRemote(g_Git
.m_CurrentDir
), this).DoModal();
1161 // CSettingGitRemote W_Remotes(m_cmdPath);
1162 // W_Remotes.DoModal();
1166 case eCmd_CreateBranch
:
1168 CString
*commitHash
= NULL
;
1169 if (selectedLeafs
.size() == 1)
1170 commitHash
= &(selectedLeafs
[0]->m_csRefHash
);
1171 CAppUtils::CreateBranchTag(false, commitHash
);
1175 case eCmd_CreateTag
:
1177 CAppUtils::CreateBranchTag(true);
1181 case eCmd_DeleteAllTags
:
1183 for (int i
= 0; i
< m_ListRefLeafs
.GetItemCount(); ++i
)
1185 m_ListRefLeafs
.SetItemState(i
, LVIS_SELECTED
, LVIS_SELECTED
);
1186 selectedLeafs
.push_back((CShadowTree
*)m_ListRefLeafs
.GetItemData(i
));
1188 if (ConfirmDeleteRef(selectedLeafs
))
1189 DoDeleteRefs(selectedLeafs
);
1198 selectedLeafs
[1]->GetRefName() + L
"^{}",
1199 selectedLeafs
[0]->GetRefName() + L
"^{}");
1203 case eCmd_UnifiedDiff
:
1205 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), selectedLeafs
[0]->m_csRefHash
, CTGitPath(), selectedLeafs
[1]->m_csRefHash
);
1208 case eCmd_EditBranchDescription
:
1211 dlg
.m_sHintText
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1212 dlg
.m_sInputText
= selectedLeafs
[0]->m_csDescription
;
1213 dlg
.m_sTitle
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1214 dlg
.m_bUseLogWidth
= true;
1215 if(dlg
.DoModal() == IDOK
)
1218 key
.Format(_T("branch.%s.description"), selectedLeafs
[0]->m_csRefName
);
1219 dlg
.m_sInputText
.Replace(_T("\r"), _T(""));
1220 dlg
.m_sInputText
.Trim();
1221 if (dlg
.m_sInputText
.IsEmpty())
1222 g_Git
.UnsetConfigValue(key
);
1224 g_Git
.SetConfigValue(key
, dlg
.m_sInputText
);
1232 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree
& leafs
, const wchar_t* from
)
1234 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
1235 if(!(*i
)->IsFrom(from
))
1240 BOOL
CBrowseRefsDlg::PreTranslateMessage(MSG
* pMsg
)
1242 if (pMsg
->message
== WM_KEYDOWN
)
1244 switch (pMsg
->wParam
)
1248 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1250 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1252 PostMessage(WM_COMMAND, IDOK);
1260 if(pMsg
->hwnd
== m_ListRefLeafs
.m_hWnd
)
1262 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1264 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1278 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
1281 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1283 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1286 if(m_currSortCol
== pNMLV
->iSubItem
)
1287 m_currSortDesc
= !m_currSortDesc
;
1290 m_currSortCol
= pNMLV
->iSubItem
;
1291 m_currSortDesc
= false;
1294 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
1295 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
1297 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
1300 void CBrowseRefsDlg::OnDestroy()
1302 if (!m_bPickedRefSet
)
1303 m_pickedRef
= GetSelectedRef(true, false);
1305 int maxcol
= m_ColumnManager
.GetColumnCount();
1306 for (int col
= 0; col
< maxcol
; ++col
)
1307 if (m_ColumnManager
.IsVisible(col
))
1308 m_ColumnManager
.ColumnResized(col
);
1309 m_ColumnManager
.WriteSettings();
1311 CResizableStandAloneDialog::OnDestroy();
1314 void CBrowseRefsDlg::OnItemChangedListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1316 LPNMLISTVIEW pNMListView
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1319 CShadowTree
*item
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(pNMListView
->iItem
);
1320 if (item
&& pNMListView
->uNewState
== LVIS_SELECTED
)
1321 m_sLastSelected
= item
->GetRefName();
1326 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
1330 if (!m_ListRefLeafs
.GetFirstSelectedItemPosition())
1335 CString
CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef
, int pickRef_Kind
, bool pickMultipleRefsOrRange
)
1337 CBrowseRefsDlg
dlg(CString(),NULL
);
1339 if(initialRef
.IsEmpty())
1340 initialRef
= L
"HEAD";
1341 dlg
.m_initialRef
= initialRef
;
1342 dlg
.m_pickRef_Kind
= pickRef_Kind
;
1343 dlg
.m_bPickOne
= !pickMultipleRefsOrRange
;
1345 if(dlg
.DoModal() != IDOK
)
1348 return dlg
.m_pickedRef
;
1351 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx
* pComboBox
, int pickRef_Kind
)
1354 pComboBox
->GetLBText(pComboBox
->GetCurSel(), origRef
);
1355 CString resultRef
= PickRef(false,origRef
,pickRef_Kind
);
1356 if(resultRef
.IsEmpty())
1358 if(wcsncmp(resultRef
,L
"refs/",5)==0)
1359 resultRef
= resultRef
.Mid(5);
1360 // if(wcsncmp(resultRef,L"heads/",6)==0)
1361 // resultRef = resultRef.Mid(6);
1363 //Find closest match of choice in combobox
1365 int matchLength
= 0;
1366 CString comboRefName
;
1367 for(int i
= 0; i
< pComboBox
->GetCount(); ++i
)
1369 pComboBox
->GetLBText(i
, comboRefName
);
1370 if(comboRefName
.Find(L
'/') < 0 && !comboRefName
.IsEmpty())
1371 comboRefName
.Insert(0,L
"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1372 if(matchLength
< comboRefName
.GetLength() && resultRef
.Right(comboRefName
.GetLength()) == comboRefName
)
1374 matchLength
= comboRefName
.GetLength();
1379 pComboBox
->SetCurSel(ixFound
);
1381 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)
1386 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1388 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1391 if(pDispInfo
->item
.pszText
== NULL
)
1392 return; //User canceled changing
1394 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1396 if(!pTree
->IsFrom(L
"refs/heads/"))
1398 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1402 CString origName
= pTree
->GetRefName().Mid(11);
1405 if(m_pListCtrlRoot
!= NULL
)
1406 newName
= m_pListCtrlRoot
->GetRefName() + L
'/';
1407 newName
+= pDispInfo
->item
.pszText
;
1409 if(wcsncmp(newName
,L
"refs/heads/",11)!=0)
1411 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1415 CString newNameTrunced
= newName
.Mid(11);
1418 if(g_Git
.Run(L
"git.exe branch -m \"" + origName
+ L
"\" \"" + newNameTrunced
+ L
"\"", &errorMsg
, CP_UTF8
) != 0)
1420 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1423 //Do as if it failed to rename. Let Refresh() do the job.
1428 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1430 // AfxMessageBox(W_csPopup);
1434 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1436 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1439 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1441 if(!pTree
->IsFrom(L
"refs/heads/"))
1443 *pResult
= TRUE
; //Dont allow renaming any other things then branches at the moment.
1448 void CBrowseRefsDlg::OnEnChangeEditFilter()
1450 SetTimer(IDT_FILTER
, 1000, NULL
);
1453 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent
)
1455 if (nIDEvent
== IDT_FILTER
)
1457 KillTimer(IDT_FILTER
);
1458 FillListCtrlForTreeNode(m_RefTreeCtrl
.GetSelectedItem());
1461 CResizableStandAloneDialog::OnTimer(nIDEvent
);
1464 LRESULT
CBrowseRefsDlg::OnClickedInfoIcon(WPARAM
/*wParam*/, LPARAM lParam
)
1466 // FIXME: x64 version would get this function called with unexpected parameters.
1470 RECT
* rect
= (LPRECT
)lParam
;
1473 point
= CPoint(rect
->left
, rect
->bottom
);
1474 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1476 if (popup
.CreatePopupMenu())
1478 temp
.LoadString(IDS_LOG_FILTER_REFNAME
);
1479 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME
), LOGFILTER_REFNAME
, temp
);
1481 temp
.LoadString(IDS_LOG_FILTER_SUBJECT
);
1482 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT
), LOGFILTER_SUBJECT
, temp
);
1484 temp
.LoadString(IDS_LOG_FILTER_AUTHORS
);
1485 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS
), LOGFILTER_AUTHORS
, temp
);
1487 temp
.LoadString(IDS_LOG_FILTER_REVS
);
1488 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS
), LOGFILTER_REVS
, temp
);
1490 int selection
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
1493 m_SelectedFilters
^= selection
;
1495 SetTimer(IDT_FILTER
, 1000, NULL
);
1501 void CBrowseRefsDlg::SetFilterCueText()
1503 CString
temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY
));
1506 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
1507 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME
));
1509 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
1511 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1513 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT
));
1516 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
1518 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1520 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS
));
1523 if (m_SelectedFilters
& LOGFILTER_REVS
)
1525 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1527 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS
));
1530 // to make the cue banner text appear more to the right of the edit control
1531 temp
= _T(" ") + temp
;
1532 m_ctrlFilter
.SetCueBanner(temp
.TrimRight());
1535 void CBrowseRefsDlg::OnBnClickedCurrentbranch()
1537 m_pickedRef
= g_Git
.GetCurrentBranch(true);
1538 m_bPickedRefSet
= true;
1542 void CBrowseRefsDlg::UpdateInfoLabel()
1545 temp
.FormatMessage(IDS_REFBROWSE_INFO
, m_ListRefLeafs
.GetItemCount(), m_ListRefLeafs
.GetSelectedCount());
1546 SetDlgItemText(IDC_INFOLABEL
, temp
);