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
),
167 CBrowseRefsDlg::~CBrowseRefsDlg()
171 void CBrowseRefsDlg::DoDataExchange(CDataExchange
* pDX
)
173 CDialog::DoDataExchange(pDX
);
174 DDX_Control(pDX
, IDC_TREE_REF
, m_RefTreeCtrl
);
175 DDX_Control(pDX
, IDC_LIST_REF_LEAFS
, m_ListRefLeafs
);
176 DDX_Control(pDX
, IDC_BROWSEREFS_EDIT_FILTER
, m_ctrlFilter
);
180 BEGIN_MESSAGE_MAP(CBrowseRefsDlg
, CResizableStandAloneDialog
)
181 ON_BN_CLICKED(IDOK
, &CBrowseRefsDlg::OnBnClickedOk
)
182 ON_NOTIFY(TVN_SELCHANGED
, IDC_TREE_REF
, &CBrowseRefsDlg::OnTvnSelchangedTreeRef
)
184 ON_NOTIFY(LVN_COLUMNCLICK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs
)
186 ON_NOTIFY(NM_DBLCLK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnNMDblclkListRefLeafs
)
187 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnItemChangedListRefLeafs
)
188 ON_NOTIFY(LVN_ENDLABELEDIT
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs
)
189 ON_NOTIFY(LVN_BEGINLABELEDIT
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs
)
190 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER
, &CBrowseRefsDlg::OnEnChangeEditFilter
)
191 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED
, OnClickedInfoIcon
)
196 // CBrowseRefsDlg message handlers
198 void CBrowseRefsDlg::OnBnClickedOk()
203 BOOL
CBrowseRefsDlg::OnInitDialog()
205 CResizableStandAloneDialog::OnInitDialog();
206 CAppUtils::MarkWindowAsUnpinnable(m_hWnd
);
208 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
209 m_ctrlFilter
.SetCancelBitmaps(IDI_CANCELNORMAL
, IDI_CANCELPRESSED
);
210 m_ctrlFilter
.SetInfoIcon(IDI_FILTEREDIT
);
213 AddAnchor(IDC_TREE_REF
, TOP_LEFT
, BOTTOM_LEFT
);
214 AddAnchor(IDC_LIST_REF_LEAFS
, TOP_LEFT
, BOTTOM_RIGHT
);
215 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER
, TOP_LEFT
);
216 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER
, TOP_LEFT
, TOP_RIGHT
);
217 AddAnchor(IDHELP
, BOTTOM_RIGHT
);
219 m_ListRefLeafs
.SetExtendedStyle(m_ListRefLeafs
.GetExtendedStyle()|LVS_EX_FULLROWSELECT
);
220 static UINT columnNames
[] = { IDS_BRANCHNAME
, IDS_DATELASTCOMMIT
, IDS_LASTCOMMIT
, IDS_LASTAUTHOR
, IDS_HASH
, IDS_DESCRIPTION
};
221 static int columnWidths
[] = { 150, 100, 300, 100, 80, 80 };
222 DWORD dwDefaultColumns
= (1 << eCol_Name
) | (1 << eCol_Date
) | (1 << eCol_Msg
) |
223 (1 << eCol_LastAuthor
) | (1 << eCol_Hash
) | (1 << eCol_Description
);
224 m_ColumnManager
.SetNames(columnNames
, _countof(columnNames
));
225 m_ColumnManager
.ReadSettings(dwDefaultColumns
, 0, _T("BrowseRefs"), _countof(columnNames
), columnWidths
);
227 AddAnchor(IDOK
,BOTTOM_RIGHT
);
228 AddAnchor(IDCANCEL
,BOTTOM_RIGHT
);
230 Refresh(m_initialRef
);
232 EnableSaveRestore(L
"BrowseRefs");
234 CString sWindowTitle
;
235 GetWindowText(sWindowTitle
);
236 CAppUtils::SetWindowTitle(m_hWnd
, g_Git
.m_CurrentDir
, sWindowTitle
);
238 m_bHasWC
= !g_GitAdminDir
.IsBareRepo(g_Git
.m_CurrentDir
);
241 m_ListRefLeafs
.ModifyStyle(0, LVS_SINGLESEL
);
243 m_ListRefLeafs
.SetFocus();
247 CShadowTree
* CShadowTree::GetNextSub(CString
& nameLeft
, bool bCreateIfNotExist
)
249 int posSlash
=nameLeft
.Find('/');
254 nameLeft
.Empty();//Nothing left
258 nameSub
=nameLeft
.Left(posSlash
);
259 nameLeft
=nameLeft
.Mid(posSlash
+1);
261 if(nameSub
.IsEmpty())
264 if(!bCreateIfNotExist
&& m_ShadowTree
.find(nameSub
)==m_ShadowTree
.end())
267 CShadowTree
& nextNode
=m_ShadowTree
[nameSub
];
268 nextNode
.m_csRefName
=nameSub
;
269 nextNode
.m_pParent
=this;
273 CShadowTree
* CShadowTree::FindLeaf(CString partialRefName
)
277 if(m_csRefName
.GetLength() > partialRefName
.GetLength())
279 if(partialRefName
.Right(m_csRefName
.GetLength()) == m_csRefName
)
281 //Match of leaf name. Try match on total name.
282 CString totalRefName
= GetRefName();
283 if(totalRefName
.Right(partialRefName
.GetLength()) == partialRefName
)
284 return this; //Also match. Found.
289 //Not a leaf. Search all nodes.
290 for(TShadowTreeMap::iterator itShadowTree
= m_ShadowTree
.begin(); itShadowTree
!= m_ShadowTree
.end(); ++itShadowTree
)
292 CShadowTree
* pSubtree
= itShadowTree
->second
.FindLeaf(partialRefName
);
294 return pSubtree
; //Found
297 return NULL
;//Not found
301 typedef std::map
<CString
,CString
> MAP_STRING_STRING
;
303 CString
CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf
, bool pickFirstSelIfMultiSel
)
305 POSITION pos
=m_ListRefLeafs
.GetFirstSelectedItemPosition();
306 //List ctrl selection?
307 if(pos
&& (pickFirstSelIfMultiSel
|| m_ListRefLeafs
.GetSelectedCount() == 1))
310 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(
311 m_ListRefLeafs
.GetNextSelectedItem(pos
));
312 return pTree
->GetRefName();
314 else if (pos
&& !pickFirstSelIfMultiSel
)
316 // at least one leaf is selected
319 while ((index
= m_ListRefLeafs
.GetNextSelectedItem(pos
)) >= 0)
321 CString ref
= ((CShadowTree
*)m_ListRefLeafs
.GetItemData(index
))->GetRefName();
322 if(wcsncmp(ref
, L
"refs/", 5) == 0)
324 if(wcsncmp(ref
, L
"heads/", 6) == 0)
326 refs
+= ref
+ _T(" ");
332 //Tree ctrl selection?
333 HTREEITEM hTree
=m_RefTreeCtrl
.GetSelectedItem();
336 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTree
);
337 return pTree
->GetRefName();
340 return CString();//None
343 static int GetBranchDescriptionsCallback(const git_config_entry
*entry
, void *data
)
345 MAP_STRING_STRING
*descriptions
= (MAP_STRING_STRING
*) data
;
346 CString key
= CUnicodeUtils::GetUnicode(entry
->name
, CP_UTF8
);
347 CString val
= CUnicodeUtils::GetUnicode(entry
->value
, CP_UTF8
);
348 descriptions
->insert(std::make_pair(key
.Mid(7, key
.GetLength() - 7 - 12), val
)); // 7: branch., 12: .description
352 MAP_STRING_STRING
GetBranchDescriptions()
354 MAP_STRING_STRING descriptions
;
356 git_config_new(&config
);
357 CStringA projectConfigA
= CUnicodeUtils::GetMulti(g_Git
.GetGitLocalConfig(), CP_UTF8
);
358 git_config_add_file_ondisk(config
, projectConfigA
.GetBuffer(), GIT_CONFIG_LEVEL_LOCAL
, FALSE
);
359 projectConfigA
.ReleaseBuffer();
360 git_config_foreach_match(config
, "branch\\..*\\.description", GetBranchDescriptionsCallback
, &descriptions
);
361 git_config_free(config
);
365 void CBrowseRefsDlg::Refresh(CString selectRef
)
368 // g_Git.GetMapHashToFriendName(m_RefMap);
370 if(!selectRef
.IsEmpty())
372 if(selectRef
== "HEAD")
374 selectRef
= g_Git
.GetSymbolicRef(selectRef
, false);
379 selectRef
= GetSelectedRef(false, true);
382 m_RefTreeCtrl
.DeleteAllItems();
383 m_ListRefLeafs
.DeleteAllItems();
384 m_TreeRoot
.m_ShadowTree
.clear();
385 m_TreeRoot
.m_csRefName
= "refs";
386 m_TreeRoot
.m_hTree
=m_RefTreeCtrl
.InsertItem(L
"Refs",NULL
,NULL
);
387 m_RefTreeCtrl
.SetItemData(m_TreeRoot
.m_hTree
,(DWORD_PTR
)&m_TreeRoot
);
389 CString allRefs
, error
;
390 if (g_Git
.Run(L
"git for-each-ref --format="
393 L
"%(authordate:relative)%04"
396 L
"%(authordate:iso8601)%03",
397 &allRefs
, &error
, CP_UTF8
))
399 CMessageBox::Show(NULL
, CString(_T("Get refs failed\n")) + error
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
405 MAP_STRING_STRING refMap
;
407 //First sort on ref name
408 while(!(singleRef
=allRefs
.Tokenize(L
"\03",linePos
)).IsEmpty())
410 singleRef
.TrimLeft(L
"\r\n");
412 CString refName
=singleRef
.Tokenize(L
"\04",valuePos
);
413 if(refName
.IsEmpty())
415 CString refRest
=singleRef
.Mid(valuePos
);
418 //Use ref based on m_pickRef_Kind
419 if(wcsncmp(refName
,L
"refs/heads",10)==0 && !(m_pickRef_Kind
& gPickRef_Head
) )
421 if(wcsncmp(refName
,L
"refs/tags",9)==0 && !(m_pickRef_Kind
& gPickRef_Tag
) )
423 if(wcsncmp(refName
,L
"refs/remotes",12)==0 && !(m_pickRef_Kind
& gPickRef_Remote
) )
426 refMap
[refName
] = refRest
; //Use
429 MAP_STRING_STRING descriptions
= GetBranchDescriptions();
432 for(MAP_STRING_STRING::iterator iterRefMap
=refMap
.begin();iterRefMap
!=refMap
.end();++iterRefMap
)
434 CShadowTree
& treeLeaf
=GetTreeNode(iterRefMap
->first
,NULL
,true);
435 CString values
=iterRefMap
->second
;
436 values
.Replace(L
"\04" L
"\04",L
"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
439 treeLeaf
.m_csRefHash
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
440 treeLeaf
.m_csDate
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
441 treeLeaf
.m_csSubject
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
442 treeLeaf
.m_csAuthor
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
443 treeLeaf
.m_csDate_Iso8601
= values
.Tokenize(L
"\04",valuePos
);
445 if (wcsncmp(iterRefMap
->first
, L
"refs/heads", 10) == 0)
446 treeLeaf
.m_csDescription
= descriptions
[treeLeaf
.m_csRefName
];
449 // try exact match first
450 if(selectRef
.IsEmpty() || !(SelectRef(selectRef
, true) || SelectRef(selectRef
, false)))
451 //Probably not on a branch. Select root node.
452 m_RefTreeCtrl
.Expand(m_TreeRoot
.m_hTree
,TVE_EXPAND
);
456 bool CBrowseRefsDlg::SelectRef(CString refName
, bool bExactMatch
)
460 CString newRefName
= GetFullRefName(refName
);
461 if(!newRefName
.IsEmpty())
462 refName
= newRefName
;
463 //else refName is not a valid ref. Try to select as good as possible.
465 if(_wcsnicmp(refName
, L
"refs/", 5) != 0)
466 return false; // Not a ref name
468 CShadowTree
& treeLeafHead
=GetTreeNode(refName
,NULL
,false);
469 if(treeLeafHead
.m_hTree
!= NULL
)
471 //Not a leaf. Select tree node and return
472 m_RefTreeCtrl
.Select(treeLeafHead
.m_hTree
,TVGN_CARET
);
476 if(treeLeafHead
.m_pParent
==NULL
)
477 return false; //Weird... should not occur.
479 //This is the current head.
480 m_RefTreeCtrl
.Select(treeLeafHead
.m_pParent
->m_hTree
,TVGN_CARET
);
482 for(int indexPos
= 0; indexPos
< m_ListRefLeafs
.GetItemCount(); ++indexPos
)
484 CShadowTree
* pCurrShadowTree
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(indexPos
);
485 if(pCurrShadowTree
== &treeLeafHead
)
487 m_ListRefLeafs
.SetItemState(indexPos
,LVIS_SELECTED
,LVIS_SELECTED
);
488 m_ListRefLeafs
.EnsureVisible(indexPos
,FALSE
);
495 CShadowTree
& CBrowseRefsDlg::GetTreeNode(CString refName
, CShadowTree
* pTreePos
, bool bCreateIfNotExist
)
499 if(_wcsnicmp(refName
, L
"refs/", 5) == 0)
500 refName
=refName
.Mid(5);
501 pTreePos
=&m_TreeRoot
;
503 if(refName
.IsEmpty())
504 return *pTreePos
;//Found leaf
506 CShadowTree
* pNextTree
=pTreePos
->GetNextSub(refName
,bCreateIfNotExist
);
509 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
510 ASSERT(!bCreateIfNotExist
);
514 if(!refName
.IsEmpty())
516 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
517 //Leafs are for the list control.
518 if(pNextTree
->m_hTree
==NULL
)
520 //New tree. Create node in control.
521 pNextTree
->m_hTree
=m_RefTreeCtrl
.InsertItem(pNextTree
->m_csRefName
,pTreePos
->m_hTree
,NULL
);
522 m_RefTreeCtrl
.SetItemData(pNextTree
->m_hTree
,(DWORD_PTR
)pNextTree
);
526 return GetTreeNode(refName
, pNextTree
, bCreateIfNotExist
);
530 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR
*pNMHDR
, LRESULT
*pResult
)
532 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
535 FillListCtrlForTreeNode(pNMTreeView
->itemNew
.hItem
);
538 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode
)
540 m_ListRefLeafs
.DeleteAllItems();
542 CShadowTree
* pTree
=(CShadowTree
*)(m_RefTreeCtrl
.GetItemData(treeNode
));
548 FillListCtrlForShadowTree(pTree
,L
"",true);
551 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree
* pTree
, CString refNamePrefix
, bool isFirstLevel
)
556 m_ctrlFilter
.GetWindowText(filter
);
558 CString ref
= refNamePrefix
+ pTree
->m_csRefName
;
559 if (!(pTree
->m_csRefName
.IsEmpty() || pTree
->m_csRefName
== "refs" && pTree
->m_pParent
== NULL
) && IsMatchFilter(pTree
, ref
, filter
))
561 int indexItem
= m_ListRefLeafs
.InsertItem(m_ListRefLeafs
.GetItemCount(), L
"");
563 m_ListRefLeafs
.SetItemData(indexItem
,(DWORD_PTR
)pTree
);
564 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Name
, ref
);
565 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Date
, pTree
->m_csDate
);
566 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Msg
, pTree
->m_csSubject
);
567 m_ListRefLeafs
.SetItemText(indexItem
,eCol_LastAuthor
, pTree
->m_csAuthor
);
568 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Hash
, pTree
->m_csRefHash
);
570 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Description
, pTree
->m_csDescription
.Tokenize(_T("\n"), pos
));
578 csThisName
=refNamePrefix
+pTree
->m_csRefName
+L
"/";
580 m_pListCtrlRoot
= pTree
;
581 for(CShadowTree::TShadowTreeMap::iterator itSubTree
=pTree
->m_ShadowTree
.begin(); itSubTree
!=pTree
->m_ShadowTree
.end(); ++itSubTree
)
583 FillListCtrlForShadowTree(&itSubTree
->second
,csThisName
,false);
588 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
589 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
591 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
595 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree
* pTree
, const CString
&ref
, const CString
&filter
)
597 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
600 msg
= msg
.MakeLower();
602 if (msg
.Find(filter
) >= 0)
606 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
608 CString msg
= pTree
->m_csSubject
;
609 msg
= msg
.MakeLower();
611 if (msg
.Find(filter
) >= 0)
615 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
617 CString msg
= pTree
->m_csAuthor
;
618 msg
= msg
.MakeLower();
620 if (msg
.Find(filter
) >= 0)
624 if (m_SelectedFilters
& LOGFILTER_REVS
)
626 CString msg
= pTree
->m_csRefHash
;
627 msg
= msg
.MakeLower();
629 if (msg
.Find(filter
) >= 0)
635 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree
& leafs
)
637 ASSERT(!leafs
.empty());
640 UINT mbIcon
=MB_ICONQUESTION
;
642 bool bIsRemoteBranch
= false;
643 bool bIsBranch
= false;
644 if (leafs
[0]->IsFrom(L
"refs/remotes")) {bIsBranch
= true; bIsRemoteBranch
= true;}
645 else if (leafs
[0]->IsFrom(L
"refs/heads")) {bIsBranch
= true;}
649 if(leafs
.size() == 1)
651 CString branchToDelete
= leafs
[0]->GetRefName().Mid(bIsRemoteBranch
? 13 : 11);
652 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, branchToDelete
);
654 //Check if branch is fully merged in HEAD
655 if (!g_Git
.IsFastForward(leafs
[0]->GetRefName(), _T("HEAD")))
657 csMessage
+= L
"\r\n\r\n";
658 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED
));
659 mbIcon
= MB_ICONWARNING
;
664 csMessage
+= L
"\r\n\r\n";
665 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
666 mbIcon
= MB_ICONWARNING
;
671 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
673 csMessage
+= L
"\r\n\r\n";
674 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK
));
675 mbIcon
= MB_ICONWARNING
;
679 csMessage
+= L
"\r\n\r\n";
680 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
681 mbIcon
= MB_ICONWARNING
;
686 else if(leafs
[0]->IsFrom(L
"refs/tags"))
688 if(leafs
.size() == 1)
690 CString tagToDelete
= leafs
[0]->GetRefName().Mid(10);
691 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, tagToDelete
);
695 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
699 return CMessageBox::Show(m_hWnd
, csMessage
, _T("TortoiseGit"), MB_YESNO
| mbIcon
) == IDYES
;
703 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree
& leafs
, bool bForce
)
705 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
706 if(!DoDeleteRef((*i
)->GetRefName(), bForce
))
711 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName
, bool bForce
)
713 bool bIsRemoteBranch
= false;
714 bool bIsBranch
= false;
715 if (wcsncmp(completeRefName
, L
"refs/remotes",12)==0) {bIsBranch
= true; bIsRemoteBranch
= true;}
716 else if (wcsncmp(completeRefName
, L
"refs/heads",10)==0) {bIsBranch
= true;}
720 CString branchToDelete
= completeRefName
.Mid(bIsRemoteBranch
? 13 : 11);
724 CString remoteName
, remoteBranchToDelete
;
725 if (SplitRemoteBranchName(branchToDelete
, remoteName
, remoteBranchToDelete
))
728 if(CAppUtils::IsSSHPutty())
730 CAppUtils::LaunchPAgent(NULL
, &remoteName
);
733 cmd
.Format(L
"git.exe push \"%s\" :refs/heads/%s", remoteName
, remoteBranchToDelete
);
736 cmd
.Format(L
"git.exe branch -%c -- %s",bForce
?L
'D':L
'd',branchToDelete
);
737 CSysProgressDlg sysProgressDlg
;
740 sysProgressDlg
.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME
)));
741 sysProgressDlg
.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS
)));
742 sysProgressDlg
.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT
)));
743 sysProgressDlg
.SetShowProgressBar(false);
744 sysProgressDlg
.ShowModal(this, true);
747 if(g_Git
.Run(cmd
,&errorMsg
,CP_UTF8
)!=0)
749 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
751 sysProgressDlg
.Stop();
756 sysProgressDlg
.Stop();
759 else if(wcsncmp(completeRefName
,L
"refs/tags",9)==0)
761 CString tagToDelete
= completeRefName
.Mid(10);
763 cmd
.Format(L
"git.exe tag -d -- %s",tagToDelete
);
765 if(g_Git
.Run(cmd
,&errorMsg
,CP_UTF8
)!=0)
767 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
774 CString
CBrowseRefsDlg::GetFullRefName(CString partialRefName
)
776 CShadowTree
* pLeaf
= m_TreeRoot
.FindLeaf(partialRefName
);
779 return pLeaf
->GetRefName();
783 void CBrowseRefsDlg::OnContextMenu(CWnd
* pWndFrom
, CPoint point
)
785 if(pWndFrom
==&m_RefTreeCtrl
) OnContextMenu_RefTreeCtrl(point
);
786 else if(pWndFrom
==&m_ListRefLeafs
) OnContextMenu_ListRefLeafs(point
);
789 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point
)
791 CPoint clientPoint
=point
;
792 m_RefTreeCtrl
.ScreenToClient(&clientPoint
);
794 HTREEITEM hTreeItem
=m_RefTreeCtrl
.HitTest(clientPoint
);
796 m_RefTreeCtrl
.Select(hTreeItem
,TVGN_CARET
);
798 VectorPShadowTree tree
;
799 ShowContextMenu(point
,hTreeItem
,tree
);
803 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point
)
805 std::vector
<CShadowTree
*> selectedLeafs
;
806 selectedLeafs
.reserve(m_ListRefLeafs
.GetSelectedCount());
807 POSITION pos
=m_ListRefLeafs
.GetFirstSelectedItemPosition();
810 selectedLeafs
.push_back(
811 (CShadowTree
*)m_ListRefLeafs
.GetItemData(
812 m_ListRefLeafs
.GetNextSelectedItem(pos
)));
815 ShowContextMenu(point
,m_RefTreeCtrl
.GetSelectedItem(),selectedLeafs
);
818 CString
GetTwoSelectedRefs(VectorPShadowTree
& selectedLeafs
, const CString
&lastSelected
, const CString
&separator
)
820 ASSERT(selectedLeafs
.size() == 2);
822 if (selectedLeafs
.at(0)->GetRefName() == lastSelected
)
823 return g_Git
.StripRefName(selectedLeafs
.at(1)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
825 return g_Git
.StripRefName(selectedLeafs
.at(0)->GetRefName()) + separator
+ g_Git
.StripRefName(lastSelected
);
828 void CBrowseRefsDlg::ShowContextMenu(CPoint point
, HTREEITEM hTreePos
, VectorPShadowTree
& selectedLeafs
)
831 popupMenu
.CreatePopupMenu();
833 bool bAddSeparator
= false;
836 if(selectedLeafs
.size()==1)
838 bAddSeparator
= true;
840 bool bShowReflogOption
= false;
841 bool bShowFetchOption
= false;
842 bool bShowSwitchOption
= 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 bShowSwitchOption
= true;
853 bShowRenameOption
= true;
854 bShowEditBranchDescriptionOption
= true;
856 else if(selectedLeafs
[0]->IsFrom(L
"refs/remotes"))
858 bShowReflogOption
= true;
859 bShowFetchOption
= true;
860 bShowCreateBranchOption
= true;
862 CString remoteBranch
;
863 if (SplitRemoteBranchName(selectedLeafs
[0]->GetRefName(), remoteName
, remoteBranch
))
864 bShowFetchOption
= false;
866 fetchFromCmd
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
868 else if(selectedLeafs
[0]->IsFrom(L
"refs/tags"))
873 temp
.LoadString(IDS_MENULOG
);
874 popupMenu
.AppendMenuIcon(eCmd_ViewLog
, temp
, IDI_LOG
);
875 popupMenu
.AppendMenuIcon(eCmd_RepoBrowser
, IDS_LOG_BROWSEREPO
, IDI_REPOBROWSE
);
876 if(bShowReflogOption
)
878 temp
.LoadString(IDS_MENUREFLOG
);
879 popupMenu
.AppendMenuIcon(eCmd_ShowReflog
, temp
, IDI_LOG
);
882 popupMenu
.AppendMenu(MF_SEPARATOR
);
883 bAddSeparator
= false;
887 bAddSeparator
= true;
888 popupMenu
.AppendMenuIcon(eCmd_Fetch
, fetchFromCmd
, IDI_PULL
);
892 popupMenu
.AppendMenu(MF_SEPARATOR
);
894 bAddSeparator
= false;
898 if (selectedLeafs
[0]->GetRefName() != _T("refs/heads/") + g_Git
.GetCurrentBranch())
900 format
.LoadString(IDS_LOG_POPUP_MERGEREV
);
901 str
.Format(format
, g_Git
.GetCurrentBranch());
902 popupMenu
.AppendMenuIcon(eCmd_Merge
, str
, IDI_MERGE
);
904 popupMenu
.AppendMenuIcon(eCmd_Switch
, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS
)), IDI_SWITCH
);
905 popupMenu
.AppendMenu(MF_SEPARATOR
);
908 if(bShowCreateBranchOption
)
910 bAddSeparator
= true;
911 temp
.LoadString(IDS_MENUBRANCH
);
912 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
915 if (bShowEditBranchDescriptionOption
)
917 bAddSeparator
= true;
918 popupMenu
.AppendMenuIcon(eCmd_EditBranchDescription
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
)), IDI_RENAME
);
920 if(bShowRenameOption
)
922 bAddSeparator
= true;
923 popupMenu
.AppendMenuIcon(eCmd_Rename
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME
)), IDI_RENAME
);
926 else if(selectedLeafs
.size() == 2)
928 bAddSeparator
= true;
929 popupMenu
.AppendMenuIcon(eCmd_Diff
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS
)), 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
[0]->m_csRefHash
,
1154 selectedLeafs
[1]->m_csRefHash
);
1158 case eCmd_EditBranchDescription
:
1161 dlg
.m_sHintText
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1162 dlg
.m_sInputText
= selectedLeafs
[0]->m_csDescription
;
1163 dlg
.m_sTitle
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1164 dlg
.m_bUseLogWidth
= true;
1165 if(dlg
.DoModal() == IDOK
)
1168 key
.Format(_T("branch.%s.description"), selectedLeafs
[0]->m_csRefName
);
1169 dlg
.m_sInputText
.Replace(_T("\r"), _T(""));
1170 dlg
.m_sInputText
.Trim();
1171 if (dlg
.m_sInputText
.IsEmpty())
1172 g_Git
.UnsetConfigValue(key
);
1174 g_Git
.SetConfigValue(key
, dlg
.m_sInputText
);
1182 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree
& leafs
, const wchar_t* from
)
1184 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
1185 if(!(*i
)->IsFrom(from
))
1190 BOOL
CBrowseRefsDlg::PreTranslateMessage(MSG
* pMsg
)
1192 if (pMsg
->message
== WM_KEYDOWN
)
1194 switch (pMsg
->wParam
)
1198 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1200 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1202 PostMessage(WM_COMMAND, IDOK);
1210 if(pMsg
->hwnd
== m_ListRefLeafs
.m_hWnd
)
1212 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1214 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1228 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
1231 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1233 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1236 if(m_currSortCol
== pNMLV
->iSubItem
)
1237 m_currSortDesc
= !m_currSortDesc
;
1240 m_currSortCol
= pNMLV
->iSubItem
;
1241 m_currSortDesc
= false;
1244 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
1245 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
1247 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
1250 void CBrowseRefsDlg::OnDestroy()
1252 m_pickedRef
= GetSelectedRef(true, false);
1254 int maxcol
= m_ColumnManager
.GetColumnCount();
1255 for (int col
= 0; col
< maxcol
; ++col
)
1256 if (m_ColumnManager
.IsVisible(col
))
1257 m_ColumnManager
.ColumnResized(col
);
1258 m_ColumnManager
.WriteSettings();
1260 CResizableStandAloneDialog::OnDestroy();
1263 void CBrowseRefsDlg::OnItemChangedListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1265 LPNMLISTVIEW pNMListView
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1268 CShadowTree
*item
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(pNMListView
->iItem
);
1269 if (item
&& pNMListView
->uNewState
== 2)
1270 m_sLastSelected
= item
->GetRefName();
1273 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
1280 CString
CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef
, int pickRef_Kind
, bool pickMultipleRefs
)
1282 CBrowseRefsDlg
dlg(CString(),NULL
);
1284 if(initialRef
.IsEmpty())
1285 initialRef
= L
"HEAD";
1286 dlg
.m_initialRef
= initialRef
;
1287 dlg
.m_pickRef_Kind
= pickRef_Kind
;
1288 dlg
.m_bPickOne
= !pickMultipleRefs
;
1290 if(dlg
.DoModal() != IDOK
)
1293 return dlg
.m_pickedRef
;
1296 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx
* pComboBox
, int pickRef_Kind
)
1299 pComboBox
->GetLBText(pComboBox
->GetCurSel(), origRef
);
1300 CString resultRef
= PickRef(false,origRef
,pickRef_Kind
);
1301 if(resultRef
.IsEmpty())
1303 if(wcsncmp(resultRef
,L
"refs/",5)==0)
1304 resultRef
= resultRef
.Mid(5);
1305 // if(wcsncmp(resultRef,L"heads/",6)==0)
1306 // resultRef = resultRef.Mid(6);
1308 //Find closest match of choice in combobox
1310 int matchLength
= 0;
1311 CString comboRefName
;
1312 for(int i
= 0; i
< pComboBox
->GetCount(); ++i
)
1314 pComboBox
->GetLBText(i
, comboRefName
);
1315 if(comboRefName
.Find(L
'/') < 0 && !comboRefName
.IsEmpty())
1316 comboRefName
.Insert(0,L
"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1317 if(matchLength
< comboRefName
.GetLength() && resultRef
.Right(comboRefName
.GetLength()) == comboRefName
)
1319 matchLength
= comboRefName
.GetLength();
1324 pComboBox
->SetCurSel(ixFound
);
1326 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)
1331 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1333 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1336 if(pDispInfo
->item
.pszText
== NULL
)
1337 return; //User canceled changing
1339 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1341 if(!pTree
->IsFrom(L
"refs/heads"))
1343 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1347 CString origName
= pTree
->GetRefName().Mid(11);
1350 if(m_pListCtrlRoot
!= NULL
)
1351 newName
= m_pListCtrlRoot
->GetRefName() + L
'/';
1352 newName
+= pDispInfo
->item
.pszText
;
1354 if(wcsncmp(newName
,L
"refs/heads/",11)!=0)
1356 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1360 CString newNameTrunced
= newName
.Mid(11);
1363 if(g_Git
.Run(L
"git branch -m \"" + origName
+ L
"\" \"" + newNameTrunced
+ L
"\"", &errorMsg
, CP_UTF8
) != 0)
1365 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1368 //Do as if it failed to rename. Let Refresh() do the job.
1373 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1375 // AfxMessageBox(W_csPopup);
1379 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1381 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1384 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1386 if(!pTree
->IsFrom(L
"refs/heads"))
1388 *pResult
= TRUE
; //Dont allow renaming any other things then branches at the moment.
1393 void CBrowseRefsDlg::OnEnChangeEditFilter()
1395 SetTimer(IDT_FILTER
, 1000, NULL
);
1398 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent
)
1400 if (nIDEvent
== IDT_FILTER
)
1402 KillTimer(IDT_FILTER
);
1403 FillListCtrlForTreeNode(m_RefTreeCtrl
.GetSelectedItem());
1406 CResizableStandAloneDialog::OnTimer(nIDEvent
);
1409 LRESULT
CBrowseRefsDlg::OnClickedInfoIcon(WPARAM
/*wParam*/, LPARAM lParam
)
1411 // FIXME: x64 version would get this function called with unexpected parameters.
1415 RECT
* rect
= (LPRECT
)lParam
;
1418 point
= CPoint(rect
->left
, rect
->bottom
);
1419 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1421 if (popup
.CreatePopupMenu())
1423 temp
.LoadString(IDS_LOG_FILTER_REFNAME
);
1424 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME
), LOGFILTER_REFNAME
, temp
);
1426 temp
.LoadString(IDS_LOG_FILTER_SUBJECT
);
1427 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT
), LOGFILTER_SUBJECT
, temp
);
1429 temp
.LoadString(IDS_LOG_FILTER_AUTHORS
);
1430 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS
), LOGFILTER_AUTHORS
, temp
);
1432 temp
.LoadString(IDS_LOG_FILTER_REVS
);
1433 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS
), LOGFILTER_REVS
, temp
);
1435 int selection
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
1438 m_SelectedFilters
^= selection
;
1440 SetTimer(IDT_FILTER
, 1000, NULL
);
1446 void CBrowseRefsDlg::SetFilterCueText()
1448 CString
temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY
));
1451 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
1452 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME
));
1454 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
1456 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1458 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT
));
1461 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
1463 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1465 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS
));
1468 if (m_SelectedFilters
& LOGFILTER_REVS
)
1470 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1472 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS
));
1475 // to make the cue banner text appear more to the right of the edit control
1476 temp
= _T(" ") + temp
;
1477 m_ctrlFilter
.SetCueBanner(temp
.TrimRight());