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_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(const CString
& left
, const 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_bPickedRefSet(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()
202 if (m_bPickOne
|| m_ListRefLeafs
.GetSelectedCount() != 2)
209 popupMenu
.CreatePopupMenu();
211 std::vector
<CShadowTree
*> selectedLeafs
;
212 GetSelectedLeaves(selectedLeafs
);
214 popupMenu
.AppendMenuIcon(1, GetSelectedRef(true, false), IDI_LOG
);
215 popupMenu
.SetDefaultItem(1);
216 popupMenu
.AppendMenuIcon(2, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")), IDI_LOG
);
217 popupMenu
.AppendMenuIcon(3, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")), IDI_LOG
);
220 GetDlgItem(IDOK
)->GetWindowRect(&rect
);
221 int selection
= popupMenu
.TrackPopupMenuEx(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, rect
.left
, rect
.top
, this, 0);
229 m_bPickedRefSet
= true;
230 m_pickedRef
= GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T(".."));
236 m_bPickedRefSet
= true;
237 m_pickedRef
= GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..."));
246 BOOL
CBrowseRefsDlg::OnInitDialog()
248 CResizableStandAloneDialog::OnInitDialog();
249 CAppUtils::MarkWindowAsUnpinnable(m_hWnd
);
251 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
252 m_ctrlFilter
.SetCancelBitmaps(IDI_CANCELNORMAL
, IDI_CANCELPRESSED
);
253 m_ctrlFilter
.SetInfoIcon(IDI_FILTEREDIT
);
256 AddAnchor(IDC_TREE_REF
, TOP_LEFT
, BOTTOM_LEFT
);
257 AddAnchor(IDC_LIST_REF_LEAFS
, TOP_LEFT
, BOTTOM_RIGHT
);
258 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER
, TOP_LEFT
);
259 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER
, TOP_LEFT
, TOP_RIGHT
);
260 AddAnchor(IDHELP
, BOTTOM_RIGHT
);
262 m_ListRefLeafs
.SetExtendedStyle(m_ListRefLeafs
.GetExtendedStyle()|LVS_EX_FULLROWSELECT
);
263 static UINT columnNames
[] = { IDS_BRANCHNAME
, IDS_DATELASTCOMMIT
, IDS_LASTCOMMIT
, IDS_LASTAUTHOR
, IDS_HASH
, IDS_DESCRIPTION
};
264 static int columnWidths
[] = { 150, 100, 300, 100, 80, 80 };
265 DWORD dwDefaultColumns
= (1 << eCol_Name
) | (1 << eCol_Date
) | (1 << eCol_Msg
) |
266 (1 << eCol_LastAuthor
) | (1 << eCol_Hash
) | (1 << eCol_Description
);
267 m_ColumnManager
.SetNames(columnNames
, _countof(columnNames
));
268 m_ColumnManager
.ReadSettings(dwDefaultColumns
, 0, _T("BrowseRefs"), _countof(columnNames
), columnWidths
);
269 m_bPickedRefSet
= false;
271 AddAnchor(IDOK
,BOTTOM_RIGHT
);
272 AddAnchor(IDCANCEL
,BOTTOM_RIGHT
);
273 AddAnchor(IDC_CURRENTBRANCH
, BOTTOM_RIGHT
);
275 Refresh(m_initialRef
);
277 EnableSaveRestore(L
"BrowseRefs");
279 CString sWindowTitle
;
280 GetWindowText(sWindowTitle
);
281 CAppUtils::SetWindowTitle(m_hWnd
, g_Git
.m_CurrentDir
, sWindowTitle
);
283 m_bHasWC
= !g_GitAdminDir
.IsBareRepo(g_Git
.m_CurrentDir
);
286 m_ListRefLeafs
.ModifyStyle(0, LVS_SINGLESEL
);
288 m_ListRefLeafs
.SetFocus();
292 CShadowTree
* CShadowTree::GetNextSub(CString
& nameLeft
, bool bCreateIfNotExist
)
294 int posSlash
=nameLeft
.Find('/');
299 nameLeft
.Empty();//Nothing left
303 nameSub
=nameLeft
.Left(posSlash
);
304 nameLeft
=nameLeft
.Mid(posSlash
+1);
306 if(nameSub
.IsEmpty())
309 if(!bCreateIfNotExist
&& m_ShadowTree
.find(nameSub
)==m_ShadowTree
.end())
312 CShadowTree
& nextNode
=m_ShadowTree
[nameSub
];
313 nextNode
.m_csRefName
=nameSub
;
314 nextNode
.m_pParent
=this;
318 CShadowTree
* CShadowTree::FindLeaf(CString partialRefName
)
322 if(m_csRefName
.GetLength() > partialRefName
.GetLength())
324 if(partialRefName
.Right(m_csRefName
.GetLength()) == m_csRefName
)
326 //Match of leaf name. Try match on total name.
327 CString totalRefName
= GetRefName();
328 if(totalRefName
.Right(partialRefName
.GetLength()) == partialRefName
)
329 return this; //Also match. Found.
334 //Not a leaf. Search all nodes.
335 for(TShadowTreeMap::iterator itShadowTree
= m_ShadowTree
.begin(); itShadowTree
!= m_ShadowTree
.end(); ++itShadowTree
)
337 CShadowTree
* pSubtree
= itShadowTree
->second
.FindLeaf(partialRefName
);
339 return pSubtree
; //Found
342 return NULL
;//Not found
346 typedef std::map
<CString
,CString
> MAP_STRING_STRING
;
348 CString
CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf
, bool pickFirstSelIfMultiSel
)
350 POSITION pos
=m_ListRefLeafs
.GetFirstSelectedItemPosition();
351 //List ctrl selection?
352 if(pos
&& (pickFirstSelIfMultiSel
|| m_ListRefLeafs
.GetSelectedCount() == 1))
355 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(
356 m_ListRefLeafs
.GetNextSelectedItem(pos
));
357 return pTree
->GetRefName();
359 else if (pos
&& !pickFirstSelIfMultiSel
)
361 // at least one leaf is selected
364 while ((index
= m_ListRefLeafs
.GetNextSelectedItem(pos
)) >= 0)
366 CString ref
= ((CShadowTree
*)m_ListRefLeafs
.GetItemData(index
))->GetRefName();
367 if(wcsncmp(ref
, L
"refs/", 5) == 0)
369 if(wcsncmp(ref
, L
"heads/", 6) == 0)
371 refs
+= ref
+ _T(" ");
377 //Tree ctrl selection?
378 HTREEITEM hTree
=m_RefTreeCtrl
.GetSelectedItem();
381 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTree
);
382 return pTree
->GetRefName();
385 return CString();//None
388 static int GetBranchDescriptionsCallback(const git_config_entry
*entry
, void *data
)
390 MAP_STRING_STRING
*descriptions
= (MAP_STRING_STRING
*) data
;
391 CString key
= CUnicodeUtils::GetUnicode(entry
->name
, CP_UTF8
);
392 CString val
= CUnicodeUtils::GetUnicode(entry
->value
, CP_UTF8
);
393 descriptions
->insert(std::make_pair(key
.Mid(7, key
.GetLength() - 7 - 12), val
)); // 7: branch., 12: .description
397 MAP_STRING_STRING
GetBranchDescriptions()
399 MAP_STRING_STRING descriptions
;
401 git_config_new(&config
);
402 git_config_add_file_ondisk(config
, CGit::GetGitPathStringA(g_Git
.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL
, FALSE
);
403 git_config_foreach_match(config
, "branch\\..*\\.description", GetBranchDescriptionsCallback
, &descriptions
);
404 git_config_free(config
);
408 void CBrowseRefsDlg::Refresh(CString selectRef
)
411 // g_Git.GetMapHashToFriendName(m_RefMap);
413 if(!selectRef
.IsEmpty())
415 if(selectRef
== "HEAD")
417 selectRef
= g_Git
.GetSymbolicRef(selectRef
, false);
422 selectRef
= GetSelectedRef(false, true);
425 m_RefTreeCtrl
.DeleteAllItems();
426 m_ListRefLeafs
.DeleteAllItems();
427 m_TreeRoot
.m_ShadowTree
.clear();
428 m_TreeRoot
.m_csRefName
= "refs";
429 m_TreeRoot
.m_hTree
=m_RefTreeCtrl
.InsertItem(L
"Refs",NULL
,NULL
);
430 m_RefTreeCtrl
.SetItemData(m_TreeRoot
.m_hTree
,(DWORD_PTR
)&m_TreeRoot
);
432 CString allRefs
, error
;
433 if (g_Git
.Run(L
"git.exe for-each-ref --format="
436 L
"%(authordate:relative)%04"
439 L
"%(authordate:iso8601)%03",
440 &allRefs
, &error
, CP_UTF8
))
442 CMessageBox::Show(NULL
, CString(_T("Get refs failed\n")) + error
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
448 MAP_STRING_STRING refMap
;
450 //First sort on ref name
451 while(!(singleRef
=allRefs
.Tokenize(L
"\03",linePos
)).IsEmpty())
453 singleRef
.TrimLeft(L
"\r\n");
455 CString refName
=singleRef
.Tokenize(L
"\04",valuePos
);
456 if(refName
.IsEmpty())
458 CString refRest
=singleRef
.Mid(valuePos
);
461 //Use ref based on m_pickRef_Kind
462 if (wcsncmp(refName
, L
"refs/heads/", 11) == 0 && !(m_pickRef_Kind
& gPickRef_Head
))
464 if (wcsncmp(refName
, L
"refs/tags/", 10) == 0 && !(m_pickRef_Kind
& gPickRef_Tag
))
466 if (wcsncmp(refName
, L
"refs/remotes/", 13) == 0 && !(m_pickRef_Kind
& gPickRef_Remote
))
469 refMap
[refName
] = refRest
; //Use
472 MAP_STRING_STRING descriptions
= GetBranchDescriptions();
475 for(MAP_STRING_STRING::iterator iterRefMap
=refMap
.begin();iterRefMap
!=refMap
.end();++iterRefMap
)
477 CShadowTree
& treeLeaf
=GetTreeNode(iterRefMap
->first
,NULL
,true);
478 CString values
=iterRefMap
->second
;
479 values
.Replace(L
"\04" L
"\04",L
"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
482 treeLeaf
.m_csRefHash
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
483 treeLeaf
.m_csDate
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
484 treeLeaf
.m_csSubject
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
485 treeLeaf
.m_csAuthor
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
486 treeLeaf
.m_csDate_Iso8601
= values
.Tokenize(L
"\04",valuePos
);
488 if (wcsncmp(iterRefMap
->first
, L
"refs/heads/", 11) == 0)
489 treeLeaf
.m_csDescription
= descriptions
[treeLeaf
.m_csRefName
];
492 // try exact match first
493 if(selectRef
.IsEmpty() || !(SelectRef(selectRef
, true) || SelectRef(selectRef
, false)))
494 //Probably not on a branch. Select root node.
495 m_RefTreeCtrl
.Expand(m_TreeRoot
.m_hTree
,TVE_EXPAND
);
499 bool CBrowseRefsDlg::SelectRef(CString refName
, bool bExactMatch
)
503 CString newRefName
= GetFullRefName(refName
);
504 if(!newRefName
.IsEmpty())
505 refName
= newRefName
;
506 //else refName is not a valid ref. Try to select as good as possible.
508 if(_wcsnicmp(refName
, L
"refs/", 5) != 0)
509 return false; // Not a ref name
511 CShadowTree
& treeLeafHead
=GetTreeNode(refName
,NULL
,false);
512 if(treeLeafHead
.m_hTree
!= NULL
)
514 //Not a leaf. Select tree node and return
515 m_RefTreeCtrl
.Select(treeLeafHead
.m_hTree
,TVGN_CARET
);
519 if(treeLeafHead
.m_pParent
==NULL
)
520 return false; //Weird... should not occur.
522 //This is the current head.
523 m_RefTreeCtrl
.Select(treeLeafHead
.m_pParent
->m_hTree
,TVGN_CARET
);
525 for(int indexPos
= 0; indexPos
< m_ListRefLeafs
.GetItemCount(); ++indexPos
)
527 CShadowTree
* pCurrShadowTree
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(indexPos
);
528 if(pCurrShadowTree
== &treeLeafHead
)
530 m_ListRefLeafs
.SetItemState(indexPos
,LVIS_SELECTED
,LVIS_SELECTED
);
531 m_ListRefLeafs
.EnsureVisible(indexPos
,FALSE
);
538 CShadowTree
& CBrowseRefsDlg::GetTreeNode(CString refName
, CShadowTree
* pTreePos
, bool bCreateIfNotExist
)
542 if(_wcsnicmp(refName
, L
"refs/", 5) == 0)
543 refName
=refName
.Mid(5);
544 pTreePos
=&m_TreeRoot
;
546 if(refName
.IsEmpty())
547 return *pTreePos
;//Found leaf
549 CShadowTree
* pNextTree
=pTreePos
->GetNextSub(refName
,bCreateIfNotExist
);
552 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
553 ASSERT(!bCreateIfNotExist
);
557 if(!refName
.IsEmpty())
559 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
560 //Leafs are for the list control.
561 if(pNextTree
->m_hTree
==NULL
)
563 //New tree. Create node in control.
564 pNextTree
->m_hTree
=m_RefTreeCtrl
.InsertItem(pNextTree
->m_csRefName
,pTreePos
->m_hTree
,NULL
);
565 m_RefTreeCtrl
.SetItemData(pNextTree
->m_hTree
,(DWORD_PTR
)pNextTree
);
569 return GetTreeNode(refName
, pNextTree
, bCreateIfNotExist
);
573 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR
*pNMHDR
, LRESULT
*pResult
)
575 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
578 FillListCtrlForTreeNode(pNMTreeView
->itemNew
.hItem
);
581 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode
)
583 m_ListRefLeafs
.DeleteAllItems();
585 CShadowTree
* pTree
=(CShadowTree
*)(m_RefTreeCtrl
.GetItemData(treeNode
));
591 FillListCtrlForShadowTree(pTree
,L
"",true);
594 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree
* pTree
, CString refNamePrefix
, bool isFirstLevel
)
599 m_ctrlFilter
.GetWindowText(filter
);
601 CString ref
= refNamePrefix
+ pTree
->m_csRefName
;
602 if (!(pTree
->m_csRefName
.IsEmpty() || pTree
->m_csRefName
== "refs" && pTree
->m_pParent
== NULL
) && IsMatchFilter(pTree
, ref
, filter
))
604 int indexItem
= m_ListRefLeafs
.InsertItem(m_ListRefLeafs
.GetItemCount(), L
"");
606 m_ListRefLeafs
.SetItemData(indexItem
,(DWORD_PTR
)pTree
);
607 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Name
, ref
);
608 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Date
, pTree
->m_csDate
);
609 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Msg
, pTree
->m_csSubject
);
610 m_ListRefLeafs
.SetItemText(indexItem
,eCol_LastAuthor
, pTree
->m_csAuthor
);
611 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Hash
, pTree
->m_csRefHash
);
613 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Description
, pTree
->m_csDescription
.Tokenize(_T("\n"), pos
));
621 csThisName
=refNamePrefix
+pTree
->m_csRefName
+L
"/";
623 m_pListCtrlRoot
= pTree
;
624 for(CShadowTree::TShadowTreeMap::iterator itSubTree
=pTree
->m_ShadowTree
.begin(); itSubTree
!=pTree
->m_ShadowTree
.end(); ++itSubTree
)
626 FillListCtrlForShadowTree(&itSubTree
->second
,csThisName
,false);
631 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
632 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
634 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
638 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree
* pTree
, const CString
&ref
, const CString
&filter
)
640 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
643 msg
= msg
.MakeLower();
645 if (msg
.Find(filter
) >= 0)
649 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
651 CString msg
= pTree
->m_csSubject
;
652 msg
= msg
.MakeLower();
654 if (msg
.Find(filter
) >= 0)
658 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
660 CString msg
= pTree
->m_csAuthor
;
661 msg
= msg
.MakeLower();
663 if (msg
.Find(filter
) >= 0)
667 if (m_SelectedFilters
& LOGFILTER_REVS
)
669 CString msg
= pTree
->m_csRefHash
;
670 msg
= msg
.MakeLower();
672 if (msg
.Find(filter
) >= 0)
678 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree
& leafs
)
680 ASSERT(!leafs
.empty());
683 UINT mbIcon
=MB_ICONQUESTION
;
685 bool bIsRemoteBranch
= false;
686 bool bIsBranch
= false;
687 if (leafs
[0]->IsFrom(L
"refs/remotes/")) {bIsBranch
= true; bIsRemoteBranch
= true;}
688 else if (leafs
[0]->IsFrom(L
"refs/heads/")) {bIsBranch
= true;}
692 if(leafs
.size() == 1)
694 CString branchToDelete
= leafs
[0]->GetRefName().Mid(bIsRemoteBranch
? 13 : 11);
695 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, branchToDelete
);
697 //Check if branch is fully merged in HEAD
698 if (!g_Git
.IsFastForward(leafs
[0]->GetRefName(), _T("HEAD")))
700 csMessage
+= L
"\r\n\r\n";
701 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED
));
702 mbIcon
= MB_ICONWARNING
;
707 csMessage
+= L
"\r\n\r\n";
708 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
709 mbIcon
= MB_ICONWARNING
;
714 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
716 csMessage
+= L
"\r\n\r\n";
717 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK
));
718 mbIcon
= MB_ICONWARNING
;
722 csMessage
+= L
"\r\n\r\n";
723 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
724 mbIcon
= MB_ICONWARNING
;
729 else if(leafs
[0]->IsFrom(L
"refs/tags/"))
731 if(leafs
.size() == 1)
733 CString tagToDelete
= leafs
[0]->GetRefName().Mid(10);
734 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, tagToDelete
);
738 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
742 return CMessageBox::Show(m_hWnd
, csMessage
, _T("TortoiseGit"), MB_YESNO
| mbIcon
) == IDYES
;
746 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree
& leafs
, bool bForce
)
748 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
749 if(!DoDeleteRef((*i
)->GetRefName(), bForce
))
754 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName
, bool bForce
)
756 bool bIsRemoteBranch
= false;
757 bool bIsBranch
= false;
758 if (wcsncmp(completeRefName
, L
"refs/remotes/",13) == 0) {bIsBranch
= true; bIsRemoteBranch
= true;}
759 else if (wcsncmp(completeRefName
, L
"refs/heads/",11) == 0) {bIsBranch
= true;}
763 CString branchToDelete
= completeRefName
.Mid(bIsRemoteBranch
? 13 : 11);
767 CString remoteName
, remoteBranchToDelete
;
768 if (SplitRemoteBranchName(branchToDelete
, remoteName
, remoteBranchToDelete
))
771 if(CAppUtils::IsSSHPutty())
773 CAppUtils::LaunchPAgent(NULL
, &remoteName
);
776 cmd
.Format(L
"git.exe push \"%s\" :refs/heads/%s", remoteName
, remoteBranchToDelete
);
779 cmd
.Format(L
"git.exe branch -%c -- %s",bForce
?L
'D':L
'd',branchToDelete
);
780 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
);
794 sysProgressDlg
.Stop();
799 sysProgressDlg
.Stop();
802 else if (wcsncmp(completeRefName
, L
"refs/tags/", 10) == 0)
804 CString tagToDelete
= completeRefName
.Mid(10);
806 cmd
.Format(L
"git.exe tag -d -- %s",tagToDelete
);
808 if(g_Git
.Run(cmd
,&errorMsg
,CP_UTF8
)!=0)
810 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
817 CString
CBrowseRefsDlg::GetFullRefName(CString partialRefName
)
819 CShadowTree
* pLeaf
= m_TreeRoot
.FindLeaf(partialRefName
);
822 return pLeaf
->GetRefName();
826 void CBrowseRefsDlg::OnContextMenu(CWnd
* pWndFrom
, CPoint point
)
828 if(pWndFrom
==&m_RefTreeCtrl
) OnContextMenu_RefTreeCtrl(point
);
829 else if(pWndFrom
==&m_ListRefLeafs
) OnContextMenu_ListRefLeafs(point
);
832 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point
)
834 CPoint clientPoint
=point
;
835 m_RefTreeCtrl
.ScreenToClient(&clientPoint
);
837 HTREEITEM hTreeItem
=m_RefTreeCtrl
.HitTest(clientPoint
);
839 m_RefTreeCtrl
.Select(hTreeItem
,TVGN_CARET
);
841 VectorPShadowTree tree
;
842 ShowContextMenu(point
,hTreeItem
,tree
);
845 void CBrowseRefsDlg::GetSelectedLeaves(VectorPShadowTree
& selectedLeafs
)
847 selectedLeafs
.reserve(m_ListRefLeafs
.GetSelectedCount());
848 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
851 selectedLeafs
.push_back((CShadowTree
*)m_ListRefLeafs
.GetItemData(m_ListRefLeafs
.GetNextSelectedItem(pos
)));
855 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point
)
857 std::vector
<CShadowTree
*> selectedLeafs
;
858 GetSelectedLeaves(selectedLeafs
);
859 ShowContextMenu(point
,m_RefTreeCtrl
.GetSelectedItem(),selectedLeafs
);
862 CString
CBrowseRefsDlg::GetTwoSelectedRefs(VectorPShadowTree
& selectedLeafs
, const CString
&lastSelected
, const CString
&separator
)
864 ASSERT(selectedLeafs
.size() == 2);
866 if (selectedLeafs
.at(0)->GetRefName() == lastSelected
)
867 return g_Git
.StripRefName(selectedLeafs
.at(1)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
869 return g_Git
.StripRefName(selectedLeafs
.at(0)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
872 void CBrowseRefsDlg::ShowContextMenu(CPoint point
, HTREEITEM hTreePos
, VectorPShadowTree
& selectedLeafs
)
875 popupMenu
.CreatePopupMenu();
877 bool bAddSeparator
= false;
880 if(selectedLeafs
.size()==1)
882 bAddSeparator
= true;
884 bool bShowReflogOption
= false;
885 bool bShowFetchOption
= false;
886 bool bShowRenameOption
= false;
887 bool bShowCreateBranchOption
= false;
888 bool bShowEditBranchDescriptionOption
= false;
890 CString fetchFromCmd
;
892 if(selectedLeafs
[0]->IsFrom(L
"refs/heads/"))
894 bShowReflogOption
= true;
895 bShowRenameOption
= true;
896 bShowEditBranchDescriptionOption
= true;
898 else if(selectedLeafs
[0]->IsFrom(L
"refs/remotes/"))
900 bShowReflogOption
= true;
901 bShowFetchOption
= true;
902 bShowCreateBranchOption
= true;
904 CString remoteBranch
;
905 if (SplitRemoteBranchName(selectedLeafs
[0]->GetRefName(), remoteName
, remoteBranch
))
906 bShowFetchOption
= false;
908 fetchFromCmd
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
910 else if(selectedLeafs
[0]->IsFrom(L
"refs/tags/"))
915 temp
.LoadString(IDS_MENULOG
);
916 popupMenu
.AppendMenuIcon(eCmd_ViewLog
, temp
, IDI_LOG
);
917 popupMenu
.AppendMenuIcon(eCmd_RepoBrowser
, IDS_LOG_BROWSEREPO
, IDI_REPOBROWSE
);
918 if(bShowReflogOption
)
920 temp
.LoadString(IDS_MENUREFLOG
);
921 popupMenu
.AppendMenuIcon(eCmd_ShowReflog
, temp
, IDI_LOG
);
924 popupMenu
.AppendMenu(MF_SEPARATOR
);
925 bAddSeparator
= false;
929 bAddSeparator
= true;
930 popupMenu
.AppendMenuIcon(eCmd_Fetch
, fetchFromCmd
, IDI_PULL
);
934 popupMenu
.AppendMenu(MF_SEPARATOR
);
936 bAddSeparator
= false;
940 if (selectedLeafs
[0]->GetRefName() != _T("refs/heads/") + g_Git
.GetCurrentBranch())
942 format
.LoadString(IDS_LOG_POPUP_MERGEREV
);
943 str
.Format(format
, g_Git
.GetCurrentBranch());
944 popupMenu
.AppendMenuIcon(eCmd_Merge
, str
, IDI_MERGE
);
946 popupMenu
.AppendMenuIcon(eCmd_Switch
, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS
)), IDI_SWITCH
);
947 popupMenu
.AppendMenu(MF_SEPARATOR
);
950 if(bShowCreateBranchOption
)
952 bAddSeparator
= true;
953 temp
.LoadString(IDS_MENUBRANCH
);
954 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
957 if (bShowEditBranchDescriptionOption
)
959 bAddSeparator
= true;
960 popupMenu
.AppendMenuIcon(eCmd_EditBranchDescription
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
)), IDI_RENAME
);
962 if(bShowRenameOption
)
964 bAddSeparator
= true;
965 popupMenu
.AppendMenuIcon(eCmd_Rename
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME
)), IDI_RENAME
);
968 else if(selectedLeafs
.size() == 2)
970 bAddSeparator
= true;
971 popupMenu
.AppendMenuIcon(eCmd_Diff
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS
)), IDI_DIFF
);
972 popupMenu
.AppendMenuIcon(eCmd_UnifiedDiff
, CString(MAKEINTRESOURCE(IDS_LOG_POPUP_GNUDIFF
)), IDI_DIFF
);
974 menu
.Format(IDS_SHOWLOG_OF
, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
975 popupMenu
.AppendMenuIcon(eCmd_ViewLogRange
, menu
, IDI_LOG
);
976 menu
.Format(IDS_SHOWLOG_OF
, GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
977 popupMenu
.AppendMenuIcon(eCmd_ViewLogRangeReachableFromOnlyOne
, menu
, IDI_LOG
);
980 if(!selectedLeafs
.empty())
982 if(AreAllFrom(selectedLeafs
, L
"refs/remotes/"))
985 popupMenu
.AppendMenu(MF_SEPARATOR
);
986 CString menuItemName
;
987 if(selectedLeafs
.size() == 1)
988 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH
);
990 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES
, selectedLeafs
.size());
992 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteBranch
, menuItemName
, IDI_DELETE
);
993 bAddSeparator
= true;
995 else if(AreAllFrom(selectedLeafs
, L
"refs/heads/"))
998 popupMenu
.AppendMenu(MF_SEPARATOR
);
999 CString menuItemName
;
1000 if(selectedLeafs
.size() == 1)
1001 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH
);
1003 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES
, selectedLeafs
.size());
1005 popupMenu
.AppendMenuIcon(eCmd_DeleteBranch
, menuItemName
, IDI_DELETE
);
1006 bAddSeparator
= true;
1008 else if(AreAllFrom(selectedLeafs
, L
"refs/tags/"))
1011 popupMenu
.AppendMenu(MF_SEPARATOR
);
1012 CString menuItemName
;
1013 if(selectedLeafs
.size() == 1)
1014 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETETAG
);
1016 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETETAGS
, selectedLeafs
.size());
1018 popupMenu
.AppendMenuIcon(eCmd_DeleteTag
, menuItemName
, IDI_DELETE
);
1019 bAddSeparator
= true;
1024 if(hTreePos
!=NULL
&& selectedLeafs
.empty())
1026 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTreePos
);
1027 if(pTree
->IsFrom(L
"refs/remotes"))
1030 popupMenu
.AppendMenu(MF_SEPARATOR
);
1031 popupMenu
.AppendMenuIcon(eCmd_ManageRemotes
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES
)), IDI_SETTINGS
);
1032 bAddSeparator
= true;
1033 if(selectedLeafs
.empty())
1035 CString remoteBranch
;
1036 if (SplitRemoteBranchName(pTree
->GetRefName(), remoteName
, remoteBranch
))
1037 remoteName
= _T("");
1038 if(!remoteName
.IsEmpty())
1041 temp
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
1042 popupMenu
.AppendMenuIcon(eCmd_Fetch
, temp
, IDI_PULL
);
1044 temp
.LoadString(IDS_DELETEREMOTETAG
);
1045 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteTag
, temp
, IDI_DELETE
);
1049 if(pTree
->IsFrom(L
"refs/heads"))
1052 popupMenu
.AppendMenu(MF_SEPARATOR
);
1054 temp
.LoadString(IDS_MENUBRANCH
);
1055 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
1057 if(pTree
->IsFrom(L
"refs/tags"))
1060 popupMenu
.AppendMenu(MF_SEPARATOR
);
1062 temp
.LoadString(IDS_MENUTAG
);
1063 popupMenu
.AppendMenuIcon(eCmd_CreateTag
, temp
, IDI_TAG
);
1064 temp
.LoadString(IDS_PROC_BROWSEREFS_DELETEALLTAGS
);
1065 popupMenu
.AppendMenuIcon(eCmd_DeleteAllTags
, temp
, IDI_DELETE
);
1070 eCmd cmd
=(eCmd
)popupMenu
.TrackPopupMenuEx(TPM_LEFTALIGN
|TPM_RETURNCMD
, point
.x
, point
.y
, this, 0);
1076 dlg
.SetRange(g_Git
.FixBranchName(selectedLeafs
[0]->GetRefName()));
1080 case eCmd_ViewLogRange
:
1083 dlg
.SetRange(GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("..")));
1087 case eCmd_ViewLogRangeReachableFromOnlyOne
:
1090 dlg
.SetRange(GetTwoSelectedRefs(selectedLeafs
, m_sLastSelected
, _T("...")));
1094 case eCmd_RepoBrowser
:
1095 CAppUtils::RunTortoiseGitProc(_T("/command:repobrowser /path:\"") + g_Git
.m_CurrentDir
+ _T("\" /rev:") + selectedLeafs
[0]->GetRefName());
1097 case eCmd_DeleteBranch
:
1098 case eCmd_DeleteRemoteBranch
:
1100 if(ConfirmDeleteRef(selectedLeafs
))
1101 DoDeleteRefs(selectedLeafs
, true);
1105 case eCmd_DeleteTag
:
1107 if(ConfirmDeleteRef(selectedLeafs
))
1108 DoDeleteRefs(selectedLeafs
, true);
1112 case eCmd_ShowReflog
:
1114 CRefLogDlg
refLogDlg(this);
1115 refLogDlg
.m_CurrentBranch
= selectedLeafs
[0]->GetRefName();
1116 refLogDlg
.DoModal();
1121 CAppUtils::Fetch(remoteName
);
1125 case eCmd_DeleteRemoteTag
:
1127 CDeleteRemoteTagDlg deleteRemoteTagDlg
;
1128 deleteRemoteTagDlg
.m_sRemote
= remoteName
;
1129 deleteRemoteTagDlg
.DoModal();
1134 CString ref
= selectedLeafs
[0]->GetRefName();
1135 CAppUtils::Merge(&ref
);
1140 CAppUtils::Switch(selectedLeafs
[0]->GetRefName());
1145 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1147 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1150 case eCmd_AddRemote
:
1152 CAddRemoteDlg(this).DoModal();
1156 case eCmd_ManageRemotes
:
1158 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS
)), new CSettingGitRemote(g_Git
.m_CurrentDir
), this).DoModal();
1159 // CSettingGitRemote W_Remotes(m_cmdPath);
1160 // W_Remotes.DoModal();
1164 case eCmd_CreateBranch
:
1166 CString
*commitHash
= NULL
;
1167 if (selectedLeafs
.size() == 1)
1168 commitHash
= &(selectedLeafs
[0]->m_csRefHash
);
1169 CAppUtils::CreateBranchTag(false, commitHash
);
1173 case eCmd_CreateTag
:
1175 CAppUtils::CreateBranchTag(true);
1179 case eCmd_DeleteAllTags
:
1181 for (int i
= 0; i
< m_ListRefLeafs
.GetItemCount(); ++i
)
1183 m_ListRefLeafs
.SetItemState(i
, LVIS_SELECTED
, LVIS_SELECTED
);
1184 selectedLeafs
.push_back((CShadowTree
*)m_ListRefLeafs
.GetItemData(i
));
1186 if (ConfirmDeleteRef(selectedLeafs
))
1187 DoDeleteRefs(selectedLeafs
, true);
1196 selectedLeafs
[1]->GetRefName() + L
"^{}",
1197 selectedLeafs
[0]->GetRefName() + L
"^{}");
1201 case eCmd_UnifiedDiff
:
1203 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), selectedLeafs
[0]->m_csRefHash
, CTGitPath(), selectedLeafs
[1]->m_csRefHash
);
1206 case eCmd_EditBranchDescription
:
1209 dlg
.m_sHintText
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1210 dlg
.m_sInputText
= selectedLeafs
[0]->m_csDescription
;
1211 dlg
.m_sTitle
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1212 dlg
.m_bUseLogWidth
= true;
1213 if(dlg
.DoModal() == IDOK
)
1216 key
.Format(_T("branch.%s.description"), selectedLeafs
[0]->m_csRefName
);
1217 dlg
.m_sInputText
.Replace(_T("\r"), _T(""));
1218 dlg
.m_sInputText
.Trim();
1219 if (dlg
.m_sInputText
.IsEmpty())
1220 g_Git
.UnsetConfigValue(key
);
1222 g_Git
.SetConfigValue(key
, dlg
.m_sInputText
);
1230 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree
& leafs
, const wchar_t* from
)
1232 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
1233 if(!(*i
)->IsFrom(from
))
1238 BOOL
CBrowseRefsDlg::PreTranslateMessage(MSG
* pMsg
)
1240 if (pMsg
->message
== WM_KEYDOWN
)
1242 switch (pMsg
->wParam
)
1246 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1248 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1250 PostMessage(WM_COMMAND, IDOK);
1258 if(pMsg
->hwnd
== m_ListRefLeafs
.m_hWnd
)
1260 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1262 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1276 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
1279 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1281 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1284 if(m_currSortCol
== pNMLV
->iSubItem
)
1285 m_currSortDesc
= !m_currSortDesc
;
1288 m_currSortCol
= pNMLV
->iSubItem
;
1289 m_currSortDesc
= false;
1292 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
1293 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
1295 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
1298 void CBrowseRefsDlg::OnDestroy()
1300 if (!m_bPickedRefSet
)
1301 m_pickedRef
= GetSelectedRef(true, false);
1303 int maxcol
= m_ColumnManager
.GetColumnCount();
1304 for (int col
= 0; col
< maxcol
; ++col
)
1305 if (m_ColumnManager
.IsVisible(col
))
1306 m_ColumnManager
.ColumnResized(col
);
1307 m_ColumnManager
.WriteSettings();
1309 CResizableStandAloneDialog::OnDestroy();
1312 void CBrowseRefsDlg::OnItemChangedListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1314 LPNMLISTVIEW pNMListView
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1317 CShadowTree
*item
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(pNMListView
->iItem
);
1318 if (item
&& pNMListView
->uNewState
== LVIS_SELECTED
)
1319 m_sLastSelected
= item
->GetRefName();
1322 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
1326 if (!m_ListRefLeafs
.GetFirstSelectedItemPosition())
1331 CString
CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef
, int pickRef_Kind
, bool pickMultipleRefsOrRange
)
1333 CBrowseRefsDlg
dlg(CString(),NULL
);
1335 if(initialRef
.IsEmpty())
1336 initialRef
= L
"HEAD";
1337 dlg
.m_initialRef
= initialRef
;
1338 dlg
.m_pickRef_Kind
= pickRef_Kind
;
1339 dlg
.m_bPickOne
= !pickMultipleRefsOrRange
;
1341 if(dlg
.DoModal() != IDOK
)
1344 return dlg
.m_pickedRef
;
1347 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx
* pComboBox
, int pickRef_Kind
)
1350 pComboBox
->GetLBText(pComboBox
->GetCurSel(), origRef
);
1351 CString resultRef
= PickRef(false,origRef
,pickRef_Kind
);
1352 if(resultRef
.IsEmpty())
1354 if(wcsncmp(resultRef
,L
"refs/",5)==0)
1355 resultRef
= resultRef
.Mid(5);
1356 // if(wcsncmp(resultRef,L"heads/",6)==0)
1357 // resultRef = resultRef.Mid(6);
1359 //Find closest match of choice in combobox
1361 int matchLength
= 0;
1362 CString comboRefName
;
1363 for(int i
= 0; i
< pComboBox
->GetCount(); ++i
)
1365 pComboBox
->GetLBText(i
, comboRefName
);
1366 if(comboRefName
.Find(L
'/') < 0 && !comboRefName
.IsEmpty())
1367 comboRefName
.Insert(0,L
"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1368 if(matchLength
< comboRefName
.GetLength() && resultRef
.Right(comboRefName
.GetLength()) == comboRefName
)
1370 matchLength
= comboRefName
.GetLength();
1375 pComboBox
->SetCurSel(ixFound
);
1377 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)
1382 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1384 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1387 if(pDispInfo
->item
.pszText
== NULL
)
1388 return; //User canceled changing
1390 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1392 if(!pTree
->IsFrom(L
"refs/heads/"))
1394 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1398 CString origName
= pTree
->GetRefName().Mid(11);
1401 if(m_pListCtrlRoot
!= NULL
)
1402 newName
= m_pListCtrlRoot
->GetRefName() + L
'/';
1403 newName
+= pDispInfo
->item
.pszText
;
1405 if(wcsncmp(newName
,L
"refs/heads/",11)!=0)
1407 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1411 CString newNameTrunced
= newName
.Mid(11);
1414 if(g_Git
.Run(L
"git.exe branch -m \"" + origName
+ L
"\" \"" + newNameTrunced
+ L
"\"", &errorMsg
, CP_UTF8
) != 0)
1416 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1419 //Do as if it failed to rename. Let Refresh() do the job.
1424 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1426 // AfxMessageBox(W_csPopup);
1430 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1432 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1435 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1437 if(!pTree
->IsFrom(L
"refs/heads/"))
1439 *pResult
= TRUE
; //Dont allow renaming any other things then branches at the moment.
1444 void CBrowseRefsDlg::OnEnChangeEditFilter()
1446 SetTimer(IDT_FILTER
, 1000, NULL
);
1449 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent
)
1451 if (nIDEvent
== IDT_FILTER
)
1453 KillTimer(IDT_FILTER
);
1454 FillListCtrlForTreeNode(m_RefTreeCtrl
.GetSelectedItem());
1457 CResizableStandAloneDialog::OnTimer(nIDEvent
);
1460 LRESULT
CBrowseRefsDlg::OnClickedInfoIcon(WPARAM
/*wParam*/, LPARAM lParam
)
1462 // FIXME: x64 version would get this function called with unexpected parameters.
1466 RECT
* rect
= (LPRECT
)lParam
;
1469 point
= CPoint(rect
->left
, rect
->bottom
);
1470 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1472 if (popup
.CreatePopupMenu())
1474 temp
.LoadString(IDS_LOG_FILTER_REFNAME
);
1475 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME
), LOGFILTER_REFNAME
, temp
);
1477 temp
.LoadString(IDS_LOG_FILTER_SUBJECT
);
1478 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT
), LOGFILTER_SUBJECT
, temp
);
1480 temp
.LoadString(IDS_LOG_FILTER_AUTHORS
);
1481 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS
), LOGFILTER_AUTHORS
, temp
);
1483 temp
.LoadString(IDS_LOG_FILTER_REVS
);
1484 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS
), LOGFILTER_REVS
, temp
);
1486 int selection
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
1489 m_SelectedFilters
^= selection
;
1491 SetTimer(IDT_FILTER
, 1000, NULL
);
1497 void CBrowseRefsDlg::SetFilterCueText()
1499 CString
temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY
));
1502 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
1503 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME
));
1505 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
1507 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1509 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT
));
1512 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
1514 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1516 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS
));
1519 if (m_SelectedFilters
& LOGFILTER_REVS
)
1521 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1523 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS
));
1526 // to make the cue banner text appear more to the right of the edit control
1527 temp
= _T(" ") + temp
;
1528 m_ctrlFilter
.SetCueBanner(temp
.TrimRight());
1531 void CBrowseRefsDlg::OnBnClickedCurrentbranch()
1533 m_pickedRef
= g_Git
.GetCurrentBranch(true);
1534 m_bPickedRefSet
= true;