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
),
161 m_ColumnManager(&m_ListRefLeafs
),
163 m_bPickCurrentBranch(false)
168 CBrowseRefsDlg::~CBrowseRefsDlg()
172 void CBrowseRefsDlg::DoDataExchange(CDataExchange
* pDX
)
174 CDialog::DoDataExchange(pDX
);
175 DDX_Control(pDX
, IDC_TREE_REF
, m_RefTreeCtrl
);
176 DDX_Control(pDX
, IDC_LIST_REF_LEAFS
, m_ListRefLeafs
);
177 DDX_Control(pDX
, IDC_BROWSEREFS_EDIT_FILTER
, m_ctrlFilter
);
181 BEGIN_MESSAGE_MAP(CBrowseRefsDlg
, CResizableStandAloneDialog
)
182 ON_BN_CLICKED(IDOK
, &CBrowseRefsDlg::OnBnClickedOk
)
183 ON_NOTIFY(TVN_SELCHANGED
, IDC_TREE_REF
, &CBrowseRefsDlg::OnTvnSelchangedTreeRef
)
185 ON_NOTIFY(LVN_COLUMNCLICK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs
)
187 ON_NOTIFY(NM_DBLCLK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnNMDblclkListRefLeafs
)
188 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnItemChangedListRefLeafs
)
189 ON_NOTIFY(LVN_ENDLABELEDIT
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs
)
190 ON_NOTIFY(LVN_BEGINLABELEDIT
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs
)
191 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER
, &CBrowseRefsDlg::OnEnChangeEditFilter
)
192 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED
, OnClickedInfoIcon
)
194 ON_BN_CLICKED(IDC_CURRENTBRANCH
, OnBnClickedCurrentbranch
)
198 // CBrowseRefsDlg message handlers
200 void CBrowseRefsDlg::OnBnClickedOk()
205 BOOL
CBrowseRefsDlg::OnInitDialog()
207 CResizableStandAloneDialog::OnInitDialog();
208 CAppUtils::MarkWindowAsUnpinnable(m_hWnd
);
210 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
211 m_ctrlFilter
.SetCancelBitmaps(IDI_CANCELNORMAL
, IDI_CANCELPRESSED
);
212 m_ctrlFilter
.SetInfoIcon(IDI_FILTEREDIT
);
215 AddAnchor(IDC_TREE_REF
, TOP_LEFT
, BOTTOM_LEFT
);
216 AddAnchor(IDC_LIST_REF_LEAFS
, TOP_LEFT
, BOTTOM_RIGHT
);
217 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER
, TOP_LEFT
);
218 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER
, TOP_LEFT
, TOP_RIGHT
);
219 AddAnchor(IDHELP
, BOTTOM_RIGHT
);
221 m_ListRefLeafs
.SetExtendedStyle(m_ListRefLeafs
.GetExtendedStyle()|LVS_EX_FULLROWSELECT
);
222 static UINT columnNames
[] = { IDS_BRANCHNAME
, IDS_DATELASTCOMMIT
, IDS_LASTCOMMIT
, IDS_LASTAUTHOR
, IDS_HASH
, IDS_DESCRIPTION
};
223 static int columnWidths
[] = { 150, 100, 300, 100, 80, 80 };
224 DWORD dwDefaultColumns
= (1 << eCol_Name
) | (1 << eCol_Date
) | (1 << eCol_Msg
) |
225 (1 << eCol_LastAuthor
) | (1 << eCol_Hash
) | (1 << eCol_Description
);
226 m_ColumnManager
.SetNames(columnNames
, _countof(columnNames
));
227 m_ColumnManager
.ReadSettings(dwDefaultColumns
, 0, _T("BrowseRefs"), _countof(columnNames
), columnWidths
);
228 m_bPickCurrentBranch
= false;
230 AddAnchor(IDOK
,BOTTOM_RIGHT
);
231 AddAnchor(IDCANCEL
,BOTTOM_RIGHT
);
233 Refresh(m_initialRef
);
235 EnableSaveRestore(L
"BrowseRefs");
237 CString sWindowTitle
;
238 GetWindowText(sWindowTitle
);
239 CAppUtils::SetWindowTitle(m_hWnd
, g_Git
.m_CurrentDir
, sWindowTitle
);
241 m_bHasWC
= !g_GitAdminDir
.IsBareRepo(g_Git
.m_CurrentDir
);
244 m_ListRefLeafs
.ModifyStyle(0, LVS_SINGLESEL
);
246 m_ListRefLeafs
.SetFocus();
250 CShadowTree
* CShadowTree::GetNextSub(CString
& nameLeft
, bool bCreateIfNotExist
)
252 int posSlash
=nameLeft
.Find('/');
257 nameLeft
.Empty();//Nothing left
261 nameSub
=nameLeft
.Left(posSlash
);
262 nameLeft
=nameLeft
.Mid(posSlash
+1);
264 if(nameSub
.IsEmpty())
267 if(!bCreateIfNotExist
&& m_ShadowTree
.find(nameSub
)==m_ShadowTree
.end())
270 CShadowTree
& nextNode
=m_ShadowTree
[nameSub
];
271 nextNode
.m_csRefName
=nameSub
;
272 nextNode
.m_pParent
=this;
276 CShadowTree
* CShadowTree::FindLeaf(CString partialRefName
)
280 if(m_csRefName
.GetLength() > partialRefName
.GetLength())
282 if(partialRefName
.Right(m_csRefName
.GetLength()) == m_csRefName
)
284 //Match of leaf name. Try match on total name.
285 CString totalRefName
= GetRefName();
286 if(totalRefName
.Right(partialRefName
.GetLength()) == partialRefName
)
287 return this; //Also match. Found.
292 //Not a leaf. Search all nodes.
293 for(TShadowTreeMap::iterator itShadowTree
= m_ShadowTree
.begin(); itShadowTree
!= m_ShadowTree
.end(); ++itShadowTree
)
295 CShadowTree
* pSubtree
= itShadowTree
->second
.FindLeaf(partialRefName
);
297 return pSubtree
; //Found
300 return NULL
;//Not found
304 typedef std::map
<CString
,CString
> MAP_STRING_STRING
;
306 CString
CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf
, bool pickFirstSelIfMultiSel
)
308 POSITION pos
=m_ListRefLeafs
.GetFirstSelectedItemPosition();
309 //List ctrl selection?
310 if(pos
&& (pickFirstSelIfMultiSel
|| m_ListRefLeafs
.GetSelectedCount() == 1))
313 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(
314 m_ListRefLeafs
.GetNextSelectedItem(pos
));
315 return pTree
->GetRefName();
317 else if (pos
&& !pickFirstSelIfMultiSel
)
319 // at least one leaf is selected
322 while ((index
= m_ListRefLeafs
.GetNextSelectedItem(pos
)) >= 0)
324 CString ref
= ((CShadowTree
*)m_ListRefLeafs
.GetItemData(index
))->GetRefName();
325 if(wcsncmp(ref
, L
"refs/", 5) == 0)
327 if(wcsncmp(ref
, L
"heads/", 6) == 0)
329 refs
+= ref
+ _T(" ");
335 //Tree ctrl selection?
336 HTREEITEM hTree
=m_RefTreeCtrl
.GetSelectedItem();
339 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTree
);
340 return pTree
->GetRefName();
343 return CString();//None
346 static int GetBranchDescriptionsCallback(const git_config_entry
*entry
, void *data
)
348 MAP_STRING_STRING
*descriptions
= (MAP_STRING_STRING
*) data
;
349 CString key
= CUnicodeUtils::GetUnicode(entry
->name
, CP_UTF8
);
350 CString val
= CUnicodeUtils::GetUnicode(entry
->value
, CP_UTF8
);
351 descriptions
->insert(std::make_pair(key
.Mid(7, key
.GetLength() - 7 - 12), val
)); // 7: branch., 12: .description
355 MAP_STRING_STRING
GetBranchDescriptions()
357 MAP_STRING_STRING descriptions
;
359 git_config_new(&config
);
360 git_config_add_file_ondisk(config
, CGit::GetGitPathStringA(g_Git
.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL
, FALSE
);
361 git_config_foreach_match(config
, "branch\\..*\\.description", GetBranchDescriptionsCallback
, &descriptions
);
362 git_config_free(config
);
366 void CBrowseRefsDlg::Refresh(CString selectRef
)
369 // g_Git.GetMapHashToFriendName(m_RefMap);
371 if(!selectRef
.IsEmpty())
373 if(selectRef
== "HEAD")
375 selectRef
= g_Git
.GetSymbolicRef(selectRef
, false);
380 selectRef
= GetSelectedRef(false, true);
383 m_RefTreeCtrl
.DeleteAllItems();
384 m_ListRefLeafs
.DeleteAllItems();
385 m_TreeRoot
.m_ShadowTree
.clear();
386 m_TreeRoot
.m_csRefName
= "refs";
387 m_TreeRoot
.m_hTree
=m_RefTreeCtrl
.InsertItem(L
"Refs",NULL
,NULL
);
388 m_RefTreeCtrl
.SetItemData(m_TreeRoot
.m_hTree
,(DWORD_PTR
)&m_TreeRoot
);
390 CString allRefs
, error
;
391 if (g_Git
.Run(L
"git.exe for-each-ref --format="
394 L
"%(authordate:relative)%04"
397 L
"%(authordate:iso8601)%03",
398 &allRefs
, &error
, CP_UTF8
))
400 CMessageBox::Show(NULL
, CString(_T("Get refs failed\n")) + error
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
406 MAP_STRING_STRING refMap
;
408 //First sort on ref name
409 while(!(singleRef
=allRefs
.Tokenize(L
"\03",linePos
)).IsEmpty())
411 singleRef
.TrimLeft(L
"\r\n");
413 CString refName
=singleRef
.Tokenize(L
"\04",valuePos
);
414 if(refName
.IsEmpty())
416 CString refRest
=singleRef
.Mid(valuePos
);
419 //Use ref based on m_pickRef_Kind
420 if (wcsncmp(refName
, L
"refs/heads/", 11) == 0 && !(m_pickRef_Kind
& gPickRef_Head
))
422 if (wcsncmp(refName
, L
"refs/tags/", 10) == 0 && !(m_pickRef_Kind
& gPickRef_Tag
))
424 if (wcsncmp(refName
, L
"refs/remotes/", 13) == 0 && !(m_pickRef_Kind
& gPickRef_Remote
))
427 refMap
[refName
] = refRest
; //Use
430 MAP_STRING_STRING descriptions
= GetBranchDescriptions();
433 for(MAP_STRING_STRING::iterator iterRefMap
=refMap
.begin();iterRefMap
!=refMap
.end();++iterRefMap
)
435 CShadowTree
& treeLeaf
=GetTreeNode(iterRefMap
->first
,NULL
,true);
436 CString values
=iterRefMap
->second
;
437 values
.Replace(L
"\04" L
"\04",L
"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
440 treeLeaf
.m_csRefHash
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
441 treeLeaf
.m_csDate
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
442 treeLeaf
.m_csSubject
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
443 treeLeaf
.m_csAuthor
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
444 treeLeaf
.m_csDate_Iso8601
= values
.Tokenize(L
"\04",valuePos
);
446 if (wcsncmp(iterRefMap
->first
, L
"refs/heads/", 11) == 0)
447 treeLeaf
.m_csDescription
= descriptions
[treeLeaf
.m_csRefName
];
450 // try exact match first
451 if(selectRef
.IsEmpty() || !(SelectRef(selectRef
, true) || SelectRef(selectRef
, false)))
452 //Probably not on a branch. Select root node.
453 m_RefTreeCtrl
.Expand(m_TreeRoot
.m_hTree
,TVE_EXPAND
);
457 bool CBrowseRefsDlg::SelectRef(CString refName
, bool bExactMatch
)
461 CString newRefName
= GetFullRefName(refName
);
462 if(!newRefName
.IsEmpty())
463 refName
= newRefName
;
464 //else refName is not a valid ref. Try to select as good as possible.
466 if(_wcsnicmp(refName
, L
"refs/", 5) != 0)
467 return false; // Not a ref name
469 CShadowTree
& treeLeafHead
=GetTreeNode(refName
,NULL
,false);
470 if(treeLeafHead
.m_hTree
!= NULL
)
472 //Not a leaf. Select tree node and return
473 m_RefTreeCtrl
.Select(treeLeafHead
.m_hTree
,TVGN_CARET
);
477 if(treeLeafHead
.m_pParent
==NULL
)
478 return false; //Weird... should not occur.
480 //This is the current head.
481 m_RefTreeCtrl
.Select(treeLeafHead
.m_pParent
->m_hTree
,TVGN_CARET
);
483 for(int indexPos
= 0; indexPos
< m_ListRefLeafs
.GetItemCount(); ++indexPos
)
485 CShadowTree
* pCurrShadowTree
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(indexPos
);
486 if(pCurrShadowTree
== &treeLeafHead
)
488 m_ListRefLeafs
.SetItemState(indexPos
,LVIS_SELECTED
,LVIS_SELECTED
);
489 m_ListRefLeafs
.EnsureVisible(indexPos
,FALSE
);
496 CShadowTree
& CBrowseRefsDlg::GetTreeNode(CString refName
, CShadowTree
* pTreePos
, bool bCreateIfNotExist
)
500 if(_wcsnicmp(refName
, L
"refs/", 5) == 0)
501 refName
=refName
.Mid(5);
502 pTreePos
=&m_TreeRoot
;
504 if(refName
.IsEmpty())
505 return *pTreePos
;//Found leaf
507 CShadowTree
* pNextTree
=pTreePos
->GetNextSub(refName
,bCreateIfNotExist
);
510 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
511 ASSERT(!bCreateIfNotExist
);
515 if(!refName
.IsEmpty())
517 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
518 //Leafs are for the list control.
519 if(pNextTree
->m_hTree
==NULL
)
521 //New tree. Create node in control.
522 pNextTree
->m_hTree
=m_RefTreeCtrl
.InsertItem(pNextTree
->m_csRefName
,pTreePos
->m_hTree
,NULL
);
523 m_RefTreeCtrl
.SetItemData(pNextTree
->m_hTree
,(DWORD_PTR
)pNextTree
);
527 return GetTreeNode(refName
, pNextTree
, bCreateIfNotExist
);
531 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR
*pNMHDR
, LRESULT
*pResult
)
533 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
536 FillListCtrlForTreeNode(pNMTreeView
->itemNew
.hItem
);
539 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode
)
541 m_ListRefLeafs
.DeleteAllItems();
543 CShadowTree
* pTree
=(CShadowTree
*)(m_RefTreeCtrl
.GetItemData(treeNode
));
549 FillListCtrlForShadowTree(pTree
,L
"",true);
552 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree
* pTree
, CString refNamePrefix
, bool isFirstLevel
)
557 m_ctrlFilter
.GetWindowText(filter
);
559 CString ref
= refNamePrefix
+ pTree
->m_csRefName
;
560 if (!(pTree
->m_csRefName
.IsEmpty() || pTree
->m_csRefName
== "refs" && pTree
->m_pParent
== NULL
) && IsMatchFilter(pTree
, ref
, filter
))
562 int indexItem
= m_ListRefLeafs
.InsertItem(m_ListRefLeafs
.GetItemCount(), L
"");
564 m_ListRefLeafs
.SetItemData(indexItem
,(DWORD_PTR
)pTree
);
565 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Name
, ref
);
566 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Date
, pTree
->m_csDate
);
567 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Msg
, pTree
->m_csSubject
);
568 m_ListRefLeafs
.SetItemText(indexItem
,eCol_LastAuthor
, pTree
->m_csAuthor
);
569 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Hash
, pTree
->m_csRefHash
);
571 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Description
, pTree
->m_csDescription
.Tokenize(_T("\n"), pos
));
579 csThisName
=refNamePrefix
+pTree
->m_csRefName
+L
"/";
581 m_pListCtrlRoot
= pTree
;
582 for(CShadowTree::TShadowTreeMap::iterator itSubTree
=pTree
->m_ShadowTree
.begin(); itSubTree
!=pTree
->m_ShadowTree
.end(); ++itSubTree
)
584 FillListCtrlForShadowTree(&itSubTree
->second
,csThisName
,false);
589 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
590 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
592 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
596 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree
* pTree
, const CString
&ref
, const CString
&filter
)
598 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
601 msg
= msg
.MakeLower();
603 if (msg
.Find(filter
) >= 0)
607 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
609 CString msg
= pTree
->m_csSubject
;
610 msg
= msg
.MakeLower();
612 if (msg
.Find(filter
) >= 0)
616 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
618 CString msg
= pTree
->m_csAuthor
;
619 msg
= msg
.MakeLower();
621 if (msg
.Find(filter
) >= 0)
625 if (m_SelectedFilters
& LOGFILTER_REVS
)
627 CString msg
= pTree
->m_csRefHash
;
628 msg
= msg
.MakeLower();
630 if (msg
.Find(filter
) >= 0)
636 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree
& leafs
)
638 ASSERT(!leafs
.empty());
641 UINT mbIcon
=MB_ICONQUESTION
;
643 bool bIsRemoteBranch
= false;
644 bool bIsBranch
= false;
645 if (leafs
[0]->IsFrom(L
"refs/remotes/")) {bIsBranch
= true; bIsRemoteBranch
= true;}
646 else if (leafs
[0]->IsFrom(L
"refs/heads/")) {bIsBranch
= true;}
650 if(leafs
.size() == 1)
652 CString branchToDelete
= leafs
[0]->GetRefName().Mid(bIsRemoteBranch
? 13 : 11);
653 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, branchToDelete
);
655 //Check if branch is fully merged in HEAD
656 if (!g_Git
.IsFastForward(leafs
[0]->GetRefName(), _T("HEAD")))
658 csMessage
+= L
"\r\n\r\n";
659 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED
));
660 mbIcon
= MB_ICONWARNING
;
665 csMessage
+= L
"\r\n\r\n";
666 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
667 mbIcon
= MB_ICONWARNING
;
672 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
674 csMessage
+= L
"\r\n\r\n";
675 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK
));
676 mbIcon
= MB_ICONWARNING
;
680 csMessage
+= L
"\r\n\r\n";
681 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
682 mbIcon
= MB_ICONWARNING
;
687 else if(leafs
[0]->IsFrom(L
"refs/tags/"))
689 if(leafs
.size() == 1)
691 CString tagToDelete
= leafs
[0]->GetRefName().Mid(10);
692 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, tagToDelete
);
696 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
700 return CMessageBox::Show(m_hWnd
, csMessage
, _T("TortoiseGit"), MB_YESNO
| mbIcon
) == IDYES
;
704 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree
& leafs
, bool bForce
)
706 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
707 if(!DoDeleteRef((*i
)->GetRefName(), bForce
))
712 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName
, bool bForce
)
714 bool bIsRemoteBranch
= false;
715 bool bIsBranch
= false;
716 if (wcsncmp(completeRefName
, L
"refs/remotes/",13) == 0) {bIsBranch
= true; bIsRemoteBranch
= true;}
717 else if (wcsncmp(completeRefName
, L
"refs/heads/",11) == 0) {bIsBranch
= true;}
721 CString branchToDelete
= completeRefName
.Mid(bIsRemoteBranch
? 13 : 11);
725 CString remoteName
, remoteBranchToDelete
;
726 if (SplitRemoteBranchName(branchToDelete
, remoteName
, remoteBranchToDelete
))
729 if(CAppUtils::IsSSHPutty())
731 CAppUtils::LaunchPAgent(NULL
, &remoteName
);
734 cmd
.Format(L
"git.exe push \"%s\" :refs/heads/%s", remoteName
, remoteBranchToDelete
);
737 cmd
.Format(L
"git.exe branch -%c -- %s",bForce
?L
'D':L
'd',branchToDelete
);
738 CSysProgressDlg sysProgressDlg
;
741 sysProgressDlg
.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME
)));
742 sysProgressDlg
.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS
)));
743 sysProgressDlg
.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT
)));
744 sysProgressDlg
.SetShowProgressBar(false);
745 sysProgressDlg
.ShowModal(this, true);
748 if(g_Git
.Run(cmd
,&errorMsg
,CP_UTF8
)!=0)
750 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
752 sysProgressDlg
.Stop();
757 sysProgressDlg
.Stop();
760 else if (wcsncmp(completeRefName
, L
"refs/tags/", 10) == 0)
762 CString tagToDelete
= completeRefName
.Mid(10);
764 cmd
.Format(L
"git.exe tag -d -- %s",tagToDelete
);
766 if(g_Git
.Run(cmd
,&errorMsg
,CP_UTF8
)!=0)
768 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
775 CString
CBrowseRefsDlg::GetFullRefName(CString partialRefName
)
777 CShadowTree
* pLeaf
= m_TreeRoot
.FindLeaf(partialRefName
);
780 return pLeaf
->GetRefName();
784 void CBrowseRefsDlg::OnContextMenu(CWnd
* pWndFrom
, CPoint point
)
786 if(pWndFrom
==&m_RefTreeCtrl
) OnContextMenu_RefTreeCtrl(point
);
787 else if(pWndFrom
==&m_ListRefLeafs
) OnContextMenu_ListRefLeafs(point
);
790 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point
)
792 CPoint clientPoint
=point
;
793 m_RefTreeCtrl
.ScreenToClient(&clientPoint
);
795 HTREEITEM hTreeItem
=m_RefTreeCtrl
.HitTest(clientPoint
);
797 m_RefTreeCtrl
.Select(hTreeItem
,TVGN_CARET
);
799 VectorPShadowTree tree
;
800 ShowContextMenu(point
,hTreeItem
,tree
);
804 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point
)
806 std::vector
<CShadowTree
*> selectedLeafs
;
807 selectedLeafs
.reserve(m_ListRefLeafs
.GetSelectedCount());
808 POSITION pos
=m_ListRefLeafs
.GetFirstSelectedItemPosition();
811 selectedLeafs
.push_back(
812 (CShadowTree
*)m_ListRefLeafs
.GetItemData(
813 m_ListRefLeafs
.GetNextSelectedItem(pos
)));
816 ShowContextMenu(point
,m_RefTreeCtrl
.GetSelectedItem(),selectedLeafs
);
819 CString
GetTwoSelectedRefs(VectorPShadowTree
& selectedLeafs
, const CString
&lastSelected
, const CString
&separator
)
821 ASSERT(selectedLeafs
.size() == 2);
823 if (selectedLeafs
.at(0)->GetRefName() == lastSelected
)
824 return g_Git
.StripRefName(selectedLeafs
.at(1)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
826 return g_Git
.StripRefName(selectedLeafs
.at(0)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
829 void CBrowseRefsDlg::ShowContextMenu(CPoint point
, HTREEITEM hTreePos
, VectorPShadowTree
& selectedLeafs
)
832 popupMenu
.CreatePopupMenu();
834 bool bAddSeparator
= false;
837 if(selectedLeafs
.size()==1)
839 bAddSeparator
= true;
841 bool bShowReflogOption
= false;
842 bool bShowFetchOption
= false;
843 bool bShowRenameOption
= false;
844 bool bShowCreateBranchOption
= false;
845 bool bShowEditBranchDescriptionOption
= false;
847 CString fetchFromCmd
;
849 if(selectedLeafs
[0]->IsFrom(L
"refs/heads/"))
851 bShowReflogOption
= true;
852 bShowRenameOption
= true;
853 bShowEditBranchDescriptionOption
= true;
855 else if(selectedLeafs
[0]->IsFrom(L
"refs/remotes/"))
857 bShowReflogOption
= true;
858 bShowFetchOption
= true;
859 bShowCreateBranchOption
= true;
861 CString remoteBranch
;
862 if (SplitRemoteBranchName(selectedLeafs
[0]->GetRefName(), remoteName
, remoteBranch
))
863 bShowFetchOption
= false;
865 fetchFromCmd
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
867 else if(selectedLeafs
[0]->IsFrom(L
"refs/tags/"))
872 temp
.LoadString(IDS_MENULOG
);
873 popupMenu
.AppendMenuIcon(eCmd_ViewLog
, temp
, IDI_LOG
);
874 popupMenu
.AppendMenuIcon(eCmd_RepoBrowser
, IDS_LOG_BROWSEREPO
, IDI_REPOBROWSE
);
875 if(bShowReflogOption
)
877 temp
.LoadString(IDS_MENUREFLOG
);
878 popupMenu
.AppendMenuIcon(eCmd_ShowReflog
, temp
, IDI_LOG
);
881 popupMenu
.AppendMenu(MF_SEPARATOR
);
882 bAddSeparator
= false;
886 bAddSeparator
= true;
887 popupMenu
.AppendMenuIcon(eCmd_Fetch
, fetchFromCmd
, IDI_PULL
);
891 popupMenu
.AppendMenu(MF_SEPARATOR
);
893 bAddSeparator
= false;
897 if (selectedLeafs
[0]->GetRefName() != _T("refs/heads/") + g_Git
.GetCurrentBranch())
899 format
.LoadString(IDS_LOG_POPUP_MERGEREV
);
900 str
.Format(format
, g_Git
.GetCurrentBranch());
901 popupMenu
.AppendMenuIcon(eCmd_Merge
, str
, IDI_MERGE
);
903 popupMenu
.AppendMenuIcon(eCmd_Switch
, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS
)), IDI_SWITCH
);
904 popupMenu
.AppendMenu(MF_SEPARATOR
);
907 if(bShowCreateBranchOption
)
909 bAddSeparator
= true;
910 temp
.LoadString(IDS_MENUBRANCH
);
911 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
914 if (bShowEditBranchDescriptionOption
)
916 bAddSeparator
= true;
917 popupMenu
.AppendMenuIcon(eCmd_EditBranchDescription
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
)), IDI_RENAME
);
919 if(bShowRenameOption
)
921 bAddSeparator
= true;
922 popupMenu
.AppendMenuIcon(eCmd_Rename
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME
)), IDI_RENAME
);
925 else if(selectedLeafs
.size() == 2)
927 bAddSeparator
= true;
928 popupMenu
.AppendMenuIcon(eCmd_Diff
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS
)), IDI_DIFF
);
929 popupMenu
.AppendMenuIcon(eCmd_UnifiedDiff
, CString(MAKEINTRESOURCE(IDS_LOG_POPUP_GNUDIFF
)), IDI_DIFF
);
931 menu
.Format(IDS_SHOWLOG_OF
, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
932 popupMenu
.AppendMenuIcon(eCmd_ViewLogRange
, menu
, IDI_LOG
);
933 menu
.Format(IDS_SHOWLOG_OF
, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
934 popupMenu
.AppendMenuIcon(eCmd_ViewLogRangeReachableFromOnlyOne
, menu
, IDI_LOG
);
937 if(!selectedLeafs
.empty())
939 if(AreAllFrom(selectedLeafs
, L
"refs/remotes/"))
942 popupMenu
.AppendMenu(MF_SEPARATOR
);
943 CString menuItemName
;
944 if(selectedLeafs
.size() == 1)
945 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH
);
947 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES
, selectedLeafs
.size());
949 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteBranch
, menuItemName
, IDI_DELETE
);
950 bAddSeparator
= true;
952 else if(AreAllFrom(selectedLeafs
, L
"refs/heads/"))
955 popupMenu
.AppendMenu(MF_SEPARATOR
);
956 CString menuItemName
;
957 if(selectedLeafs
.size() == 1)
958 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH
);
960 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES
, selectedLeafs
.size());
962 popupMenu
.AppendMenuIcon(eCmd_DeleteBranch
, menuItemName
, IDI_DELETE
);
963 bAddSeparator
= true;
965 else if(AreAllFrom(selectedLeafs
, L
"refs/tags/"))
968 popupMenu
.AppendMenu(MF_SEPARATOR
);
969 CString menuItemName
;
970 if(selectedLeafs
.size() == 1)
971 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETETAG
);
973 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETETAGS
, selectedLeafs
.size());
975 popupMenu
.AppendMenuIcon(eCmd_DeleteTag
, menuItemName
, IDI_DELETE
);
976 bAddSeparator
= true;
981 if(hTreePos
!=NULL
&& selectedLeafs
.empty())
983 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTreePos
);
984 if(pTree
->IsFrom(L
"refs/remotes"))
987 popupMenu
.AppendMenu(MF_SEPARATOR
);
988 popupMenu
.AppendMenuIcon(eCmd_ManageRemotes
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES
)), IDI_SETTINGS
);
989 bAddSeparator
= true;
990 if(selectedLeafs
.empty())
992 CString remoteBranch
;
993 if (SplitRemoteBranchName(pTree
->GetRefName(), remoteName
, remoteBranch
))
995 if(!remoteName
.IsEmpty())
998 temp
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
999 popupMenu
.AppendMenuIcon(eCmd_Fetch
, temp
, IDI_PULL
);
1001 temp
.LoadString(IDS_DELETEREMOTETAG
);
1002 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteTag
, temp
, IDI_DELETE
);
1006 if(pTree
->IsFrom(L
"refs/heads"))
1009 popupMenu
.AppendMenu(MF_SEPARATOR
);
1011 temp
.LoadString(IDS_MENUBRANCH
);
1012 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
1014 if(pTree
->IsFrom(L
"refs/tags"))
1017 popupMenu
.AppendMenu(MF_SEPARATOR
);
1019 temp
.LoadString(IDS_MENUTAG
);
1020 popupMenu
.AppendMenuIcon(eCmd_CreateTag
, temp
, IDI_TAG
);
1021 temp
.LoadString(IDS_PROC_BROWSEREFS_DELETEALLTAGS
);
1022 popupMenu
.AppendMenuIcon(eCmd_DeleteAllTags
, temp
, IDI_DELETE
);
1027 eCmd cmd
=(eCmd
)popupMenu
.TrackPopupMenuEx(TPM_LEFTALIGN
|TPM_RETURNCMD
, point
.x
, point
.y
, this, 0);
1033 dlg
.SetRange(g_Git
.FixBranchName(selectedLeafs
[0]->GetRefName()));
1037 case eCmd_ViewLogRange
:
1040 dlg
.SetRange(GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
1044 case eCmd_ViewLogRangeReachableFromOnlyOne
:
1047 dlg
.SetRange(GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
1051 case eCmd_RepoBrowser
:
1052 CAppUtils::RunTortoiseGitProc(_T("/command:repobrowser /path:\"") + g_Git
.m_CurrentDir
+ _T("\" /rev:") + selectedLeafs
[0]->GetRefName());
1054 case eCmd_DeleteBranch
:
1055 case eCmd_DeleteRemoteBranch
:
1057 if(ConfirmDeleteRef(selectedLeafs
))
1058 DoDeleteRefs(selectedLeafs
, true);
1062 case eCmd_DeleteTag
:
1064 if(ConfirmDeleteRef(selectedLeafs
))
1065 DoDeleteRefs(selectedLeafs
, true);
1069 case eCmd_ShowReflog
:
1071 CRefLogDlg
refLogDlg(this);
1072 refLogDlg
.m_CurrentBranch
= selectedLeafs
[0]->GetRefName();
1073 refLogDlg
.DoModal();
1078 CAppUtils::Fetch(remoteName
);
1082 case eCmd_DeleteRemoteTag
:
1084 CDeleteRemoteTagDlg deleteRemoteTagDlg
;
1085 deleteRemoteTagDlg
.m_sRemote
= remoteName
;
1086 deleteRemoteTagDlg
.DoModal();
1091 CString ref
= selectedLeafs
[0]->GetRefName();
1092 CAppUtils::Merge(&ref
);
1097 CAppUtils::Switch(selectedLeafs
[0]->GetRefName());
1102 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1104 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1107 case eCmd_AddRemote
:
1109 CAddRemoteDlg(this).DoModal();
1113 case eCmd_ManageRemotes
:
1115 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS
)), new CSettingGitRemote(g_Git
.m_CurrentDir
), this).DoModal();
1116 // CSettingGitRemote W_Remotes(m_cmdPath);
1117 // W_Remotes.DoModal();
1121 case eCmd_CreateBranch
:
1123 CString
*commitHash
= NULL
;
1124 if (selectedLeafs
.size() == 1)
1125 commitHash
= &(selectedLeafs
[0]->m_csRefHash
);
1126 CAppUtils::CreateBranchTag(false, commitHash
);
1130 case eCmd_CreateTag
:
1132 CAppUtils::CreateBranchTag(true);
1136 case eCmd_DeleteAllTags
:
1138 for (int i
= 0; i
< m_ListRefLeafs
.GetItemCount(); ++i
)
1140 m_ListRefLeafs
.SetItemState(i
, LVIS_SELECTED
, LVIS_SELECTED
);
1141 selectedLeafs
.push_back((CShadowTree
*)m_ListRefLeafs
.GetItemData(i
));
1143 if (ConfirmDeleteRef(selectedLeafs
))
1144 DoDeleteRefs(selectedLeafs
, true);
1153 selectedLeafs
[1]->GetRefName() + L
"^{}",
1154 selectedLeafs
[0]->GetRefName() + L
"^{}");
1158 case eCmd_UnifiedDiff
:
1160 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), selectedLeafs
[0]->m_csRefHash
, CTGitPath(), selectedLeafs
[1]->m_csRefHash
);
1163 case eCmd_EditBranchDescription
:
1166 dlg
.m_sHintText
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1167 dlg
.m_sInputText
= selectedLeafs
[0]->m_csDescription
;
1168 dlg
.m_sTitle
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1169 dlg
.m_bUseLogWidth
= true;
1170 if(dlg
.DoModal() == IDOK
)
1173 key
.Format(_T("branch.%s.description"), selectedLeafs
[0]->m_csRefName
);
1174 dlg
.m_sInputText
.Replace(_T("\r"), _T(""));
1175 dlg
.m_sInputText
.Trim();
1176 if (dlg
.m_sInputText
.IsEmpty())
1177 g_Git
.UnsetConfigValue(key
);
1179 g_Git
.SetConfigValue(key
, dlg
.m_sInputText
);
1187 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree
& leafs
, const wchar_t* from
)
1189 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
1190 if(!(*i
)->IsFrom(from
))
1195 BOOL
CBrowseRefsDlg::PreTranslateMessage(MSG
* pMsg
)
1197 if (pMsg
->message
== WM_KEYDOWN
)
1199 switch (pMsg
->wParam
)
1203 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1205 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1207 PostMessage(WM_COMMAND, IDOK);
1215 if(pMsg
->hwnd
== m_ListRefLeafs
.m_hWnd
)
1217 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1219 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1233 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
1236 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1238 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1241 if(m_currSortCol
== pNMLV
->iSubItem
)
1242 m_currSortDesc
= !m_currSortDesc
;
1245 m_currSortCol
= pNMLV
->iSubItem
;
1246 m_currSortDesc
= false;
1249 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
1250 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
1252 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
1255 void CBrowseRefsDlg::OnDestroy()
1257 if (m_bPickCurrentBranch
)
1258 m_pickedRef
= g_Git
.GetCurrentBranch(true);
1260 m_pickedRef
= GetSelectedRef(true, false);
1262 int maxcol
= m_ColumnManager
.GetColumnCount();
1263 for (int col
= 0; col
< maxcol
; ++col
)
1264 if (m_ColumnManager
.IsVisible(col
))
1265 m_ColumnManager
.ColumnResized(col
);
1266 m_ColumnManager
.WriteSettings();
1268 CResizableStandAloneDialog::OnDestroy();
1271 void CBrowseRefsDlg::OnItemChangedListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1273 LPNMLISTVIEW pNMListView
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1276 CShadowTree
*item
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(pNMListView
->iItem
);
1277 if (item
&& pNMListView
->uNewState
== 2)
1278 m_sLastSelected
= item
->GetRefName();
1281 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
1288 CString
CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef
, int pickRef_Kind
, bool pickMultipleRefs
)
1290 CBrowseRefsDlg
dlg(CString(),NULL
);
1292 if(initialRef
.IsEmpty())
1293 initialRef
= L
"HEAD";
1294 dlg
.m_initialRef
= initialRef
;
1295 dlg
.m_pickRef_Kind
= pickRef_Kind
;
1296 dlg
.m_bPickOne
= !pickMultipleRefs
;
1298 if(dlg
.DoModal() != IDOK
)
1301 return dlg
.m_pickedRef
;
1304 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx
* pComboBox
, int pickRef_Kind
)
1307 pComboBox
->GetLBText(pComboBox
->GetCurSel(), origRef
);
1308 CString resultRef
= PickRef(false,origRef
,pickRef_Kind
);
1309 if(resultRef
.IsEmpty())
1311 if(wcsncmp(resultRef
,L
"refs/",5)==0)
1312 resultRef
= resultRef
.Mid(5);
1313 // if(wcsncmp(resultRef,L"heads/",6)==0)
1314 // resultRef = resultRef.Mid(6);
1316 //Find closest match of choice in combobox
1318 int matchLength
= 0;
1319 CString comboRefName
;
1320 for(int i
= 0; i
< pComboBox
->GetCount(); ++i
)
1322 pComboBox
->GetLBText(i
, comboRefName
);
1323 if(comboRefName
.Find(L
'/') < 0 && !comboRefName
.IsEmpty())
1324 comboRefName
.Insert(0,L
"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1325 if(matchLength
< comboRefName
.GetLength() && resultRef
.Right(comboRefName
.GetLength()) == comboRefName
)
1327 matchLength
= comboRefName
.GetLength();
1332 pComboBox
->SetCurSel(ixFound
);
1334 ASSERT(FALSE
);//No match found. So either pickRef_Kind is wrong or the combobox does not contain the ref specified in the picker (which it should unless the repo has changed before creating the CBrowseRef dialog)
1339 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1341 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1344 if(pDispInfo
->item
.pszText
== NULL
)
1345 return; //User canceled changing
1347 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1349 if(!pTree
->IsFrom(L
"refs/heads/"))
1351 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1355 CString origName
= pTree
->GetRefName().Mid(11);
1358 if(m_pListCtrlRoot
!= NULL
)
1359 newName
= m_pListCtrlRoot
->GetRefName() + L
'/';
1360 newName
+= pDispInfo
->item
.pszText
;
1362 if(wcsncmp(newName
,L
"refs/heads/",11)!=0)
1364 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1368 CString newNameTrunced
= newName
.Mid(11);
1371 if(g_Git
.Run(L
"git.exe branch -m \"" + origName
+ L
"\" \"" + newNameTrunced
+ L
"\"", &errorMsg
, CP_UTF8
) != 0)
1373 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1376 //Do as if it failed to rename. Let Refresh() do the job.
1381 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1383 // AfxMessageBox(W_csPopup);
1387 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1389 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1392 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1394 if(!pTree
->IsFrom(L
"refs/heads/"))
1396 *pResult
= TRUE
; //Dont allow renaming any other things then branches at the moment.
1401 void CBrowseRefsDlg::OnEnChangeEditFilter()
1403 SetTimer(IDT_FILTER
, 1000, NULL
);
1406 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent
)
1408 if (nIDEvent
== IDT_FILTER
)
1410 KillTimer(IDT_FILTER
);
1411 FillListCtrlForTreeNode(m_RefTreeCtrl
.GetSelectedItem());
1414 CResizableStandAloneDialog::OnTimer(nIDEvent
);
1417 LRESULT
CBrowseRefsDlg::OnClickedInfoIcon(WPARAM
/*wParam*/, LPARAM lParam
)
1419 // FIXME: x64 version would get this function called with unexpected parameters.
1423 RECT
* rect
= (LPRECT
)lParam
;
1426 point
= CPoint(rect
->left
, rect
->bottom
);
1427 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1429 if (popup
.CreatePopupMenu())
1431 temp
.LoadString(IDS_LOG_FILTER_REFNAME
);
1432 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME
), LOGFILTER_REFNAME
, temp
);
1434 temp
.LoadString(IDS_LOG_FILTER_SUBJECT
);
1435 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT
), LOGFILTER_SUBJECT
, temp
);
1437 temp
.LoadString(IDS_LOG_FILTER_AUTHORS
);
1438 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS
), LOGFILTER_AUTHORS
, temp
);
1440 temp
.LoadString(IDS_LOG_FILTER_REVS
);
1441 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS
), LOGFILTER_REVS
, temp
);
1443 int selection
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
1446 m_SelectedFilters
^= selection
;
1448 SetTimer(IDT_FILTER
, 1000, NULL
);
1454 void CBrowseRefsDlg::SetFilterCueText()
1456 CString
temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY
));
1459 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
1460 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME
));
1462 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
1464 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1466 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT
));
1469 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
1471 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1473 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS
));
1476 if (m_SelectedFilters
& LOGFILTER_REVS
)
1478 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1480 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS
));
1483 // to make the cue banner text appear more to the right of the edit control
1484 temp
= _T(" ") + temp
;
1485 m_ctrlFilter
.SetCueBanner(temp
.TrimRight());
1488 void CBrowseRefsDlg::OnBnClickedCurrentbranch()
1490 m_bPickCurrentBranch
= true;