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
;
402 CAutoConfig
config(true);
403 git_config_add_file_ondisk(config
, CGit::GetGitPathStringA(g_Git
.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL
, FALSE
);
404 git_config_foreach_match(config
, "branch\\..*\\.description", GetBranchDescriptionsCallback
, &descriptions
);
408 void CBrowseRefsDlg::Refresh(CString selectRef
)
411 // g_Git.GetMapHashToFriendName(m_RefMap);
413 if(!selectRef
.IsEmpty())
415 if(selectRef
== "HEAD")
417 if (g_Git
.GetCurrentBranchFromFile(g_Git
.m_CurrentDir
, selectRef
))
420 selectRef
= L
"refs/heads/" + selectRef
;
425 selectRef
= GetSelectedRef(false, true);
428 m_RefTreeCtrl
.DeleteAllItems();
429 m_ListRefLeafs
.DeleteAllItems();
430 m_TreeRoot
.m_ShadowTree
.clear();
431 m_TreeRoot
.m_csRefName
= "refs";
432 m_TreeRoot
.m_hTree
=m_RefTreeCtrl
.InsertItem(L
"refs",NULL
,NULL
);
433 m_RefTreeCtrl
.SetItemData(m_TreeRoot
.m_hTree
,(DWORD_PTR
)&m_TreeRoot
);
435 CString allRefs
, error
;
436 if (g_Git
.Run(L
"git.exe for-each-ref --format="
440 L
"%(authordate:relative)%04"
443 L
"%(authordate:iso8601)%03",
444 &allRefs
, &error
, CP_UTF8
))
446 CMessageBox::Show(NULL
, CString(_T("Get refs failed\n")) + error
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
452 MAP_STRING_STRING refMap
;
454 //First sort on ref name
455 while(!(singleRef
=allRefs
.Tokenize(L
"\03",linePos
)).IsEmpty())
457 singleRef
.TrimLeft(L
"\r\n");
459 CString refName
=singleRef
.Tokenize(L
"\04",valuePos
);
460 if(refName
.IsEmpty())
462 CString refRest
=singleRef
.Mid(valuePos
);
465 //Use ref based on m_pickRef_Kind
466 if (wcsncmp(refName
, L
"refs/heads/", 11) == 0 && !(m_pickRef_Kind
& gPickRef_Head
))
468 if (wcsncmp(refName
, L
"refs/tags/", 10) == 0 && !(m_pickRef_Kind
& gPickRef_Tag
))
470 if (wcsncmp(refName
, L
"refs/remotes/", 13) == 0 && !(m_pickRef_Kind
& gPickRef_Remote
))
473 refMap
[refName
] = refRest
; //Use
476 MAP_STRING_STRING descriptions
= GetBranchDescriptions();
479 for(MAP_STRING_STRING::iterator iterRefMap
=refMap
.begin();iterRefMap
!=refMap
.end();++iterRefMap
)
481 CShadowTree
& treeLeaf
=GetTreeNode(iterRefMap
->first
,NULL
,true);
482 CString values
=iterRefMap
->second
;
483 values
.Replace(L
"\04" L
"\04",L
"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
486 treeLeaf
.m_csRefHash
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
487 treeLeaf
.m_csUpstream
= values
.Tokenize(L
"\04", valuePos
); if (valuePos
< 0) continue;
488 CGit::GetShortName(treeLeaf
.m_csUpstream
, treeLeaf
.m_csUpstream
, L
"refs/remotes/");
489 treeLeaf
.m_csDate
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
490 treeLeaf
.m_csSubject
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
491 treeLeaf
.m_csAuthor
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
492 treeLeaf
.m_csDate_Iso8601
= values
.Tokenize(L
"\04",valuePos
);
494 if (wcsncmp(iterRefMap
->first
, L
"refs/heads/", 11) == 0)
495 treeLeaf
.m_csDescription
= descriptions
[treeLeaf
.m_csRefName
];
498 // always expand the tree first
499 m_RefTreeCtrl
.Expand(m_TreeRoot
.m_hTree
, TVE_EXPAND
);
501 // try exact match first
502 if (!selectRef
.IsEmpty() && !SelectRef(selectRef
, true))
503 SelectRef(selectRef
, false);
506 bool CBrowseRefsDlg::SelectRef(CString refName
, bool bExactMatch
)
510 CString newRefName
= GetFullRefName(refName
);
511 if(!newRefName
.IsEmpty())
512 refName
= newRefName
;
513 //else refName is not a valid ref. Try to select as good as possible.
515 if(_wcsnicmp(refName
, L
"refs/", 5) != 0)
516 return false; // Not a ref name
518 CShadowTree
& treeLeafHead
=GetTreeNode(refName
,NULL
,false);
519 if(treeLeafHead
.m_hTree
!= NULL
)
521 //Not a leaf. Select tree node and return
522 m_RefTreeCtrl
.Select(treeLeafHead
.m_hTree
,TVGN_CARET
);
526 if(treeLeafHead
.m_pParent
==NULL
)
527 return false; //Weird... should not occur.
529 //This is the current head.
530 m_RefTreeCtrl
.Select(treeLeafHead
.m_pParent
->m_hTree
,TVGN_CARET
);
532 for(int indexPos
= 0; indexPos
< m_ListRefLeafs
.GetItemCount(); ++indexPos
)
534 CShadowTree
* pCurrShadowTree
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(indexPos
);
535 if(pCurrShadowTree
== &treeLeafHead
)
537 m_ListRefLeafs
.SetItemState(indexPos
,LVIS_SELECTED
,LVIS_SELECTED
);
538 m_ListRefLeafs
.EnsureVisible(indexPos
,FALSE
);
545 CShadowTree
& CBrowseRefsDlg::GetTreeNode(CString refName
, CShadowTree
* pTreePos
, bool bCreateIfNotExist
)
549 if(_wcsnicmp(refName
, L
"refs/", 5) == 0)
550 refName
=refName
.Mid(5);
551 pTreePos
=&m_TreeRoot
;
553 if(refName
.IsEmpty())
554 return *pTreePos
;//Found leaf
556 CShadowTree
* pNextTree
=pTreePos
->GetNextSub(refName
,bCreateIfNotExist
);
559 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
560 ASSERT(!bCreateIfNotExist
);
564 if(!refName
.IsEmpty())
566 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
567 //Leafs are for the list control.
568 if(pNextTree
->m_hTree
==NULL
)
570 //New tree. Create node in control.
571 pNextTree
->m_hTree
=m_RefTreeCtrl
.InsertItem(pNextTree
->m_csRefName
,pTreePos
->m_hTree
,NULL
);
572 m_RefTreeCtrl
.SetItemData(pNextTree
->m_hTree
,(DWORD_PTR
)pNextTree
);
576 return GetTreeNode(refName
, pNextTree
, bCreateIfNotExist
);
580 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR
*pNMHDR
, LRESULT
*pResult
)
582 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
585 FillListCtrlForTreeNode(pNMTreeView
->itemNew
.hItem
);
588 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode
)
590 m_ListRefLeafs
.DeleteAllItems();
592 CShadowTree
* pTree
=(CShadowTree
*)(m_RefTreeCtrl
.GetItemData(treeNode
));
598 FillListCtrlForShadowTree(pTree
,L
"",true);
599 m_ColumnManager
.SetVisible(eCol_Upstream
, (wcsncmp(pTree
->GetRefName(), L
"refs/heads", 11) == 0));
602 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree
* pTree
, CString refNamePrefix
, bool isFirstLevel
)
607 m_ctrlFilter
.GetWindowText(filter
);
609 CString ref
= refNamePrefix
+ pTree
->m_csRefName
;
610 if (!(pTree
->m_csRefName
.IsEmpty() || pTree
->m_csRefName
== "refs" && pTree
->m_pParent
== NULL
) && IsMatchFilter(pTree
, ref
, filter
))
612 int indexItem
= m_ListRefLeafs
.InsertItem(m_ListRefLeafs
.GetItemCount(), L
"");
614 m_ListRefLeafs
.SetItemData(indexItem
,(DWORD_PTR
)pTree
);
615 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Name
, ref
);
616 m_ListRefLeafs
.SetItemText(indexItem
, eCol_Upstream
, pTree
->m_csUpstream
);
617 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Date
, pTree
->m_csDate
);
618 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Msg
, pTree
->m_csSubject
);
619 m_ListRefLeafs
.SetItemText(indexItem
,eCol_LastAuthor
, pTree
->m_csAuthor
);
620 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Hash
, pTree
->m_csRefHash
);
622 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Description
, pTree
->m_csDescription
.Tokenize(_T("\n"), pos
));
630 csThisName
=refNamePrefix
+pTree
->m_csRefName
+L
"/";
632 m_pListCtrlRoot
= pTree
;
633 for(CShadowTree::TShadowTreeMap::iterator itSubTree
=pTree
->m_ShadowTree
.begin(); itSubTree
!=pTree
->m_ShadowTree
.end(); ++itSubTree
)
635 FillListCtrlForShadowTree(&itSubTree
->second
,csThisName
,false);
640 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
641 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
643 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
647 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree
* pTree
, const CString
&ref
, const CString
&filter
)
649 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
652 msg
= msg
.MakeLower();
654 if (msg
.Find(filter
) >= 0)
658 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
660 CString msg
= pTree
->m_csSubject
;
661 msg
= msg
.MakeLower();
663 if (msg
.Find(filter
) >= 0)
667 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
669 CString msg
= pTree
->m_csAuthor
;
670 msg
= msg
.MakeLower();
672 if (msg
.Find(filter
) >= 0)
676 if (m_SelectedFilters
& LOGFILTER_REVS
)
678 CString msg
= pTree
->m_csRefHash
;
679 msg
= msg
.MakeLower();
681 if (msg
.Find(filter
) >= 0)
687 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree
& leafs
)
689 ASSERT(!leafs
.empty());
692 UINT mbIcon
=MB_ICONQUESTION
;
694 bool bIsRemoteBranch
= false;
695 bool bIsBranch
= false;
696 if (leafs
[0]->IsFrom(L
"refs/remotes/")) {bIsBranch
= true; bIsRemoteBranch
= true;}
697 else if (leafs
[0]->IsFrom(L
"refs/heads/")) {bIsBranch
= true;}
701 if(leafs
.size() == 1)
703 CString branchToDelete
= leafs
[0]->GetRefName().Mid(bIsRemoteBranch
? 13 : 11);
704 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, branchToDelete
);
706 //Check if branch is fully merged in HEAD
707 if (!g_Git
.IsFastForward(leafs
[0]->GetRefName(), _T("HEAD")))
709 csMessage
+= L
"\r\n\r\n";
710 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED
));
711 mbIcon
= MB_ICONWARNING
;
716 csMessage
+= L
"\r\n\r\n";
717 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
718 mbIcon
= MB_ICONWARNING
;
723 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
725 csMessage
+= L
"\r\n\r\n";
726 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK
));
727 mbIcon
= MB_ICONWARNING
;
731 csMessage
+= L
"\r\n\r\n";
732 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
733 mbIcon
= MB_ICONWARNING
;
738 else if(leafs
[0]->IsFrom(L
"refs/tags/"))
740 if(leafs
.size() == 1)
742 CString tagToDelete
= leafs
[0]->GetRefName().Mid(10);
743 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, tagToDelete
);
747 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
751 return CMessageBox::Show(m_hWnd
, csMessage
, _T("TortoiseGit"), MB_YESNO
| mbIcon
) == IDYES
;
755 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree
& leafs
)
757 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
758 if(!DoDeleteRef((*i
)->GetRefName()))
763 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName
)
765 bool bIsRemoteBranch
= false;
766 bool bIsBranch
= false;
767 if (wcsncmp(completeRefName
, L
"refs/remotes/",13) == 0) {bIsBranch
= true; bIsRemoteBranch
= true;}
768 else if (wcsncmp(completeRefName
, L
"refs/heads/",11) == 0) {bIsBranch
= true;}
772 CString branchToDelete
= completeRefName
.Mid(13);
774 CString remoteName
, remoteBranchToDelete
;
775 if (SplitRemoteBranchName(branchToDelete
, remoteName
, remoteBranchToDelete
))
778 if (CAppUtils::IsSSHPutty())
779 CAppUtils::LaunchPAgent(NULL
, &remoteName
);
781 cmd
.Format(L
"git.exe push \"%s\" :refs/heads/%s", remoteName
, remoteBranchToDelete
);
783 CSysProgressDlg sysProgressDlg
;
784 sysProgressDlg
.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME
)));
785 sysProgressDlg
.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS
)));
786 sysProgressDlg
.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT
)));
787 sysProgressDlg
.SetShowProgressBar(false);
788 sysProgressDlg
.ShowModal(this, true);
791 if (g_Git
.Run(cmd
, &errorMsg
, CP_UTF8
) != 0)
793 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
794 sysProgressDlg
.Stop();
798 sysProgressDlg
.Stop();
803 if (g_Git
.DeleteRef(completeRefName
))
805 CMessageBox::Show(m_hWnd
, g_Git
.GetGitLastErr(L
"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH
), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
809 else if (wcsncmp(completeRefName
, L
"refs/tags/", 10) == 0)
811 if (g_Git
.DeleteRef(completeRefName
))
813 CMessageBox::Show(m_hWnd
, g_Git
.GetGitLastErr(L
"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH
), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
820 CString
CBrowseRefsDlg::GetFullRefName(CString partialRefName
)
822 CShadowTree
* pLeaf
= m_TreeRoot
.FindLeaf(partialRefName
);
825 return pLeaf
->GetRefName();
829 void CBrowseRefsDlg::OnContextMenu(CWnd
* pWndFrom
, CPoint point
)
831 if(pWndFrom
==&m_RefTreeCtrl
) OnContextMenu_RefTreeCtrl(point
);
832 else if(pWndFrom
==&m_ListRefLeafs
) OnContextMenu_ListRefLeafs(point
);
835 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point
)
837 CPoint clientPoint
=point
;
838 m_RefTreeCtrl
.ScreenToClient(&clientPoint
);
840 HTREEITEM hTreeItem
=m_RefTreeCtrl
.HitTest(clientPoint
);
842 m_RefTreeCtrl
.Select(hTreeItem
,TVGN_CARET
);
844 VectorPShadowTree tree
;
845 ShowContextMenu(point
,hTreeItem
,tree
);
848 void CBrowseRefsDlg::GetSelectedLeaves(VectorPShadowTree
& selectedLeafs
)
850 selectedLeafs
.reserve(m_ListRefLeafs
.GetSelectedCount());
851 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
854 selectedLeafs
.push_back((CShadowTree
*)m_ListRefLeafs
.GetItemData(m_ListRefLeafs
.GetNextSelectedItem(pos
)));
858 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point
)
860 std::vector
<CShadowTree
*> selectedLeafs
;
861 GetSelectedLeaves(selectedLeafs
);
862 ShowContextMenu(point
,m_RefTreeCtrl
.GetSelectedItem(),selectedLeafs
);
865 CString
CBrowseRefsDlg::GetTwoSelectedRefs(VectorPShadowTree
& selectedLeafs
, const CString
&lastSelected
, const CString
&separator
)
867 ASSERT(selectedLeafs
.size() == 2);
869 if (selectedLeafs
.at(0)->GetRefName() == lastSelected
)
870 return g_Git
.StripRefName(selectedLeafs
.at(1)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
872 return g_Git
.StripRefName(selectedLeafs
.at(0)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
875 void CBrowseRefsDlg::ShowContextMenu(CPoint point
, HTREEITEM hTreePos
, VectorPShadowTree
& selectedLeafs
)
878 popupMenu
.CreatePopupMenu();
880 bool bAddSeparator
= false;
883 if(selectedLeafs
.size()==1)
885 bAddSeparator
= true;
887 bool bShowReflogOption
= false;
888 bool bShowFetchOption
= false;
889 bool bShowRenameOption
= false;
890 bool bShowCreateBranchOption
= false;
891 bool bShowEditBranchDescriptionOption
= false;
893 CString fetchFromCmd
;
895 if(selectedLeafs
[0]->IsFrom(L
"refs/heads/"))
897 bShowReflogOption
= true;
898 bShowRenameOption
= true;
899 bShowEditBranchDescriptionOption
= true;
901 else if(selectedLeafs
[0]->IsFrom(L
"refs/remotes/"))
903 bShowReflogOption
= true;
904 bShowFetchOption
= true;
905 bShowCreateBranchOption
= true;
907 CString remoteBranch
;
908 if (SplitRemoteBranchName(selectedLeafs
[0]->GetRefName(), remoteName
, remoteBranch
))
909 bShowFetchOption
= false;
911 fetchFromCmd
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
913 else if(selectedLeafs
[0]->IsFrom(L
"refs/tags/"))
918 temp
.LoadString(IDS_MENULOG
);
919 popupMenu
.AppendMenuIcon(eCmd_ViewLog
, temp
, IDI_LOG
);
920 popupMenu
.AppendMenuIcon(eCmd_RepoBrowser
, IDS_LOG_BROWSEREPO
, IDI_REPOBROWSE
);
921 if(bShowReflogOption
)
923 temp
.LoadString(IDS_MENUREFLOG
);
924 popupMenu
.AppendMenuIcon(eCmd_ShowReflog
, temp
, IDI_LOG
);
927 popupMenu
.AppendMenu(MF_SEPARATOR
);
928 bAddSeparator
= false;
932 bAddSeparator
= true;
933 popupMenu
.AppendMenuIcon(eCmd_Fetch
, fetchFromCmd
, IDI_PULL
);
937 popupMenu
.AppendMenu(MF_SEPARATOR
);
939 bAddSeparator
= false;
943 if (selectedLeafs
[0]->GetRefName() != _T("refs/heads/") + g_Git
.GetCurrentBranch())
945 format
.LoadString(IDS_LOG_POPUP_MERGEREV
);
946 str
.Format(format
, g_Git
.GetCurrentBranch());
947 popupMenu
.AppendMenuIcon(eCmd_Merge
, str
, IDI_MERGE
);
949 popupMenu
.AppendMenuIcon(eCmd_Switch
, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS
)), IDI_SWITCH
);
950 popupMenu
.AppendMenu(MF_SEPARATOR
);
953 if(bShowCreateBranchOption
)
955 bAddSeparator
= true;
956 temp
.LoadString(IDS_MENUBRANCH
);
957 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
960 if (bShowEditBranchDescriptionOption
)
962 bAddSeparator
= true;
963 popupMenu
.AppendMenuIcon(eCmd_EditBranchDescription
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
)), IDI_RENAME
);
965 if(bShowRenameOption
)
967 bAddSeparator
= true;
968 popupMenu
.AppendMenuIcon(eCmd_Rename
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME
)), IDI_RENAME
);
971 else if(selectedLeafs
.size() == 2)
973 bAddSeparator
= true;
974 popupMenu
.AppendMenuIcon(eCmd_Diff
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS
)), IDI_DIFF
);
975 popupMenu
.AppendMenuIcon(eCmd_UnifiedDiff
, CString(MAKEINTRESOURCE(IDS_LOG_POPUP_GNUDIFF
)), IDI_DIFF
);
977 menu
.Format(IDS_SHOWLOG_OF
, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
978 popupMenu
.AppendMenuIcon(eCmd_ViewLogRange
, menu
, IDI_LOG
);
979 menu
.Format(IDS_SHOWLOG_OF
, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
980 popupMenu
.AppendMenuIcon(eCmd_ViewLogRangeReachableFromOnlyOne
, menu
, IDI_LOG
);
983 if(!selectedLeafs
.empty())
985 if(AreAllFrom(selectedLeafs
, L
"refs/remotes/"))
988 popupMenu
.AppendMenu(MF_SEPARATOR
);
989 CString menuItemName
;
990 if(selectedLeafs
.size() == 1)
991 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH
);
993 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES
, selectedLeafs
.size());
995 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteBranch
, menuItemName
, IDI_DELETE
);
996 bAddSeparator
= true;
998 else if(AreAllFrom(selectedLeafs
, L
"refs/heads/"))
1001 popupMenu
.AppendMenu(MF_SEPARATOR
);
1002 CString menuItemName
;
1003 if(selectedLeafs
.size() == 1)
1004 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH
);
1006 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES
, selectedLeafs
.size());
1008 popupMenu
.AppendMenuIcon(eCmd_DeleteBranch
, menuItemName
, IDI_DELETE
);
1009 bAddSeparator
= true;
1011 else if(AreAllFrom(selectedLeafs
, L
"refs/tags/"))
1014 popupMenu
.AppendMenu(MF_SEPARATOR
);
1015 CString menuItemName
;
1016 if(selectedLeafs
.size() == 1)
1017 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETETAG
);
1019 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETETAGS
, selectedLeafs
.size());
1021 popupMenu
.AppendMenuIcon(eCmd_DeleteTag
, menuItemName
, IDI_DELETE
);
1022 bAddSeparator
= true;
1027 if(hTreePos
!=NULL
&& selectedLeafs
.empty())
1029 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTreePos
);
1030 if(pTree
->IsFrom(L
"refs/remotes"))
1033 popupMenu
.AppendMenu(MF_SEPARATOR
);
1034 popupMenu
.AppendMenuIcon(eCmd_ManageRemotes
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES
)), IDI_SETTINGS
);
1035 bAddSeparator
= true;
1036 if(selectedLeafs
.empty())
1038 CString remoteBranch
;
1039 if (SplitRemoteBranchName(pTree
->GetRefName(), remoteName
, remoteBranch
))
1040 remoteName
= _T("");
1041 if(!remoteName
.IsEmpty())
1044 temp
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
1045 popupMenu
.AppendMenuIcon(eCmd_Fetch
, temp
, IDI_PULL
);
1047 temp
.LoadString(IDS_DELETEREMOTETAG
);
1048 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteTag
, temp
, IDI_DELETE
);
1052 if(pTree
->IsFrom(L
"refs/heads"))
1055 popupMenu
.AppendMenu(MF_SEPARATOR
);
1057 temp
.LoadString(IDS_MENUBRANCH
);
1058 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
1060 if(pTree
->IsFrom(L
"refs/tags"))
1063 popupMenu
.AppendMenu(MF_SEPARATOR
);
1065 temp
.LoadString(IDS_MENUTAG
);
1066 popupMenu
.AppendMenuIcon(eCmd_CreateTag
, temp
, IDI_TAG
);
1067 temp
.LoadString(IDS_PROC_BROWSEREFS_DELETEALLTAGS
);
1068 popupMenu
.AppendMenuIcon(eCmd_DeleteAllTags
, temp
, IDI_DELETE
);
1073 eCmd cmd
=(eCmd
)popupMenu
.TrackPopupMenuEx(TPM_LEFTALIGN
|TPM_RETURNCMD
, point
.x
, point
.y
, this, 0);
1079 dlg
.SetRange(g_Git
.FixBranchName(selectedLeafs
[0]->GetRefName()));
1083 case eCmd_ViewLogRange
:
1086 dlg
.SetRange(GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
1090 case eCmd_ViewLogRangeReachableFromOnlyOne
:
1093 dlg
.SetRange(GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
1097 case eCmd_RepoBrowser
:
1098 CAppUtils::RunTortoiseGitProc(_T("/command:repobrowser /path:\"") + g_Git
.m_CurrentDir
+ _T("\" /rev:") + selectedLeafs
[0]->GetRefName());
1100 case eCmd_DeleteBranch
:
1101 case eCmd_DeleteRemoteBranch
:
1103 if(ConfirmDeleteRef(selectedLeafs
))
1104 DoDeleteRefs(selectedLeafs
);
1108 case eCmd_DeleteTag
:
1110 if(ConfirmDeleteRef(selectedLeafs
))
1111 DoDeleteRefs(selectedLeafs
);
1115 case eCmd_ShowReflog
:
1117 CRefLogDlg
refLogDlg(this);
1118 refLogDlg
.m_CurrentBranch
= selectedLeafs
[0]->GetRefName();
1119 refLogDlg
.DoModal();
1124 CAppUtils::Fetch(remoteName
);
1128 case eCmd_DeleteRemoteTag
:
1130 CDeleteRemoteTagDlg deleteRemoteTagDlg
;
1131 deleteRemoteTagDlg
.m_sRemote
= remoteName
;
1132 deleteRemoteTagDlg
.DoModal();
1137 CString ref
= selectedLeafs
[0]->GetRefName();
1138 CAppUtils::Merge(&ref
);
1143 CAppUtils::Switch(selectedLeafs
[0]->GetRefName());
1148 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1150 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1153 case eCmd_AddRemote
:
1155 CAddRemoteDlg(this).DoModal();
1159 case eCmd_ManageRemotes
:
1161 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS
)), new CSettingGitRemote(g_Git
.m_CurrentDir
), this).DoModal();
1162 // CSettingGitRemote W_Remotes(m_cmdPath);
1163 // W_Remotes.DoModal();
1167 case eCmd_CreateBranch
:
1169 CString
*commitHash
= NULL
;
1170 if (selectedLeafs
.size() == 1)
1171 commitHash
= &(selectedLeafs
[0]->m_csRefHash
);
1172 CAppUtils::CreateBranchTag(false, commitHash
);
1176 case eCmd_CreateTag
:
1178 CAppUtils::CreateBranchTag(true);
1182 case eCmd_DeleteAllTags
:
1184 for (int i
= 0; i
< m_ListRefLeafs
.GetItemCount(); ++i
)
1186 m_ListRefLeafs
.SetItemState(i
, LVIS_SELECTED
, LVIS_SELECTED
);
1187 selectedLeafs
.push_back((CShadowTree
*)m_ListRefLeafs
.GetItemData(i
));
1189 if (ConfirmDeleteRef(selectedLeafs
))
1190 DoDeleteRefs(selectedLeafs
);
1199 selectedLeafs
[1]->GetRefName() + L
"^{}",
1200 selectedLeafs
[0]->GetRefName() + L
"^{}");
1204 case eCmd_UnifiedDiff
:
1206 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), selectedLeafs
[0]->m_csRefHash
, CTGitPath(), selectedLeafs
[1]->m_csRefHash
);
1209 case eCmd_EditBranchDescription
:
1212 dlg
.m_sHintText
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1213 dlg
.m_sInputText
= selectedLeafs
[0]->m_csDescription
;
1214 dlg
.m_sTitle
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1215 dlg
.m_bUseLogWidth
= true;
1216 if(dlg
.DoModal() == IDOK
)
1219 key
.Format(_T("branch.%s.description"), selectedLeafs
[0]->m_csRefName
);
1220 dlg
.m_sInputText
.Replace(_T("\r"), _T(""));
1221 dlg
.m_sInputText
.Trim();
1222 if (dlg
.m_sInputText
.IsEmpty())
1223 g_Git
.UnsetConfigValue(key
);
1225 g_Git
.SetConfigValue(key
, dlg
.m_sInputText
);
1233 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree
& leafs
, const wchar_t* from
)
1235 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
1236 if(!(*i
)->IsFrom(from
))
1241 BOOL
CBrowseRefsDlg::PreTranslateMessage(MSG
* pMsg
)
1243 if (pMsg
->message
== WM_KEYDOWN
)
1245 switch (pMsg
->wParam
)
1249 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1251 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1253 PostMessage(WM_COMMAND, IDOK);
1261 if(pMsg
->hwnd
== m_ListRefLeafs
.m_hWnd
)
1263 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1265 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1279 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
1282 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1284 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1287 if(m_currSortCol
== pNMLV
->iSubItem
)
1288 m_currSortDesc
= !m_currSortDesc
;
1291 m_currSortCol
= pNMLV
->iSubItem
;
1292 m_currSortDesc
= false;
1295 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
1296 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
1298 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
1301 void CBrowseRefsDlg::OnDestroy()
1303 if (!m_bPickedRefSet
)
1304 m_pickedRef
= GetSelectedRef(true, false);
1306 int maxcol
= m_ColumnManager
.GetColumnCount();
1307 for (int col
= 0; col
< maxcol
; ++col
)
1308 if (m_ColumnManager
.IsVisible(col
))
1309 m_ColumnManager
.ColumnResized(col
);
1310 m_ColumnManager
.WriteSettings();
1312 CResizableStandAloneDialog::OnDestroy();
1315 void CBrowseRefsDlg::OnItemChangedListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1317 LPNMLISTVIEW pNMListView
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1320 CShadowTree
*item
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(pNMListView
->iItem
);
1321 if (item
&& pNMListView
->uNewState
== LVIS_SELECTED
)
1322 m_sLastSelected
= item
->GetRefName();
1327 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
1331 if (!m_ListRefLeafs
.GetFirstSelectedItemPosition())
1336 CString
CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef
, int pickRef_Kind
, bool pickMultipleRefsOrRange
)
1338 CBrowseRefsDlg
dlg(CString(),NULL
);
1340 if(initialRef
.IsEmpty())
1341 initialRef
= L
"HEAD";
1342 dlg
.m_initialRef
= initialRef
;
1343 dlg
.m_pickRef_Kind
= pickRef_Kind
;
1344 dlg
.m_bPickOne
= !pickMultipleRefsOrRange
;
1346 if(dlg
.DoModal() != IDOK
)
1349 return dlg
.m_pickedRef
;
1352 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx
* pComboBox
, int pickRef_Kind
)
1355 pComboBox
->GetLBText(pComboBox
->GetCurSel(), origRef
);
1356 CString resultRef
= PickRef(false,origRef
,pickRef_Kind
);
1357 if(resultRef
.IsEmpty())
1359 if(wcsncmp(resultRef
,L
"refs/",5)==0)
1360 resultRef
= resultRef
.Mid(5);
1361 // if(wcsncmp(resultRef,L"heads/",6)==0)
1362 // resultRef = resultRef.Mid(6);
1364 //Find closest match of choice in combobox
1366 int matchLength
= 0;
1367 CString comboRefName
;
1368 for(int i
= 0; i
< pComboBox
->GetCount(); ++i
)
1370 pComboBox
->GetLBText(i
, comboRefName
);
1371 if(comboRefName
.Find(L
'/') < 0 && !comboRefName
.IsEmpty())
1372 comboRefName
.Insert(0,L
"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1373 if(matchLength
< comboRefName
.GetLength() && resultRef
.Right(comboRefName
.GetLength()) == comboRefName
)
1375 matchLength
= comboRefName
.GetLength();
1380 pComboBox
->SetCurSel(ixFound
);
1382 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)
1387 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1389 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1392 if(pDispInfo
->item
.pszText
== NULL
)
1393 return; //User canceled changing
1395 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1397 if(!pTree
->IsFrom(L
"refs/heads/"))
1399 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1403 CString origName
= pTree
->GetRefName().Mid(11);
1406 if(m_pListCtrlRoot
!= NULL
)
1407 newName
= m_pListCtrlRoot
->GetRefName() + L
'/';
1408 newName
+= pDispInfo
->item
.pszText
;
1410 if(wcsncmp(newName
,L
"refs/heads/",11)!=0)
1412 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1416 CString newNameTrunced
= newName
.Mid(11);
1419 if(g_Git
.Run(L
"git.exe branch -m \"" + origName
+ L
"\" \"" + newNameTrunced
+ L
"\"", &errorMsg
, CP_UTF8
) != 0)
1421 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1424 //Do as if it failed to rename. Let Refresh() do the job.
1429 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1431 // AfxMessageBox(W_csPopup);
1435 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1437 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1440 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1442 if(!pTree
->IsFrom(L
"refs/heads/"))
1444 *pResult
= TRUE
; //Dont allow renaming any other things then branches at the moment.
1449 void CBrowseRefsDlg::OnEnChangeEditFilter()
1451 SetTimer(IDT_FILTER
, 1000, NULL
);
1454 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent
)
1456 if (nIDEvent
== IDT_FILTER
)
1458 KillTimer(IDT_FILTER
);
1459 FillListCtrlForTreeNode(m_RefTreeCtrl
.GetSelectedItem());
1462 CResizableStandAloneDialog::OnTimer(nIDEvent
);
1465 LRESULT
CBrowseRefsDlg::OnClickedInfoIcon(WPARAM
/*wParam*/, LPARAM lParam
)
1467 // FIXME: x64 version would get this function called with unexpected parameters.
1471 RECT
* rect
= (LPRECT
)lParam
;
1474 point
= CPoint(rect
->left
, rect
->bottom
);
1475 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1477 if (popup
.CreatePopupMenu())
1479 temp
.LoadString(IDS_LOG_FILTER_REFNAME
);
1480 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME
), LOGFILTER_REFNAME
, temp
);
1482 temp
.LoadString(IDS_LOG_FILTER_SUBJECT
);
1483 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT
), LOGFILTER_SUBJECT
, temp
);
1485 temp
.LoadString(IDS_LOG_FILTER_AUTHORS
);
1486 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS
), LOGFILTER_AUTHORS
, temp
);
1488 temp
.LoadString(IDS_LOG_FILTER_REVS
);
1489 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS
), LOGFILTER_REVS
, temp
);
1491 int selection
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
1494 m_SelectedFilters
^= selection
;
1496 SetTimer(IDT_FILTER
, 1000, NULL
);
1502 void CBrowseRefsDlg::SetFilterCueText()
1504 CString
temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY
));
1507 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
1508 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME
));
1510 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
1512 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1514 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT
));
1517 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
1519 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1521 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS
));
1524 if (m_SelectedFilters
& LOGFILTER_REVS
)
1526 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1528 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS
));
1531 // to make the cue banner text appear more to the right of the edit control
1532 temp
= _T(" ") + temp
;
1533 m_ctrlFilter
.SetCueBanner(temp
.TrimRight());
1536 void CBrowseRefsDlg::OnBnClickedCurrentbranch()
1538 m_pickedRef
= g_Git
.GetCurrentBranch(true);
1539 m_bPickedRefSet
= true;
1543 void CBrowseRefsDlg::UpdateInfoLabel()
1546 temp
.FormatMessage(IDS_REFBROWSE_INFO
, m_ListRefLeafs
.GetItemCount(), m_ListRefLeafs
.GetSelectedCount());
1547 SetDlgItemText(IDC_INFOLABEL
, temp
);