1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2012 - 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"
36 #include "UnicodeUtils.h"
39 void SetSortArrow(CListCtrl
* control
, int nColumn
, bool bAscending
)
44 CHeaderCtrl
* pHeader
= control
->GetHeaderCtrl();
45 HDITEM HeaderItem
= {0};
46 HeaderItem
.mask
= HDI_FORMAT
;
47 for (int i
=0; i
<pHeader
->GetItemCount(); ++i
)
49 pHeader
->GetItem(i
, &HeaderItem
);
50 HeaderItem
.fmt
&= ~(HDF_SORTDOWN
| HDF_SORTUP
);
51 pHeader
->SetItem(i
, &HeaderItem
);
55 pHeader
->GetItem(nColumn
, &HeaderItem
);
56 HeaderItem
.fmt
|= (bAscending
? HDF_SORTUP
: HDF_SORTDOWN
);
57 pHeader
->SetItem(nColumn
, &HeaderItem
);
61 class CRefLeafListCompareFunc
64 CRefLeafListCompareFunc(CListCtrl
* pList
, int col
, bool desc
):m_col(col
),m_desc(desc
),m_pList(pList
){
65 m_bSortLogical
= !CRegDWORD(L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER
);
67 m_bSortLogical
= !CRegDWORD(L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE
);
70 static int CALLBACK
StaticCompare(LPARAM lParam1
, LPARAM lParam2
, LPARAM lParamSort
)
72 return ((CRefLeafListCompareFunc
*)lParamSort
)->Compare(lParam1
,lParam2
);
75 int Compare(LPARAM lParam1
, LPARAM lParam2
)
78 (CShadowTree
*)m_pList
->GetItemData((int)lParam1
),
79 (CShadowTree
*)m_pList
->GetItemData((int)lParam2
));
82 int Compare(CShadowTree
* pLeft
, CShadowTree
* pRight
)
84 int result
=CompareNoDesc(pLeft
,pRight
);
90 int CompareNoDesc(CShadowTree
* pLeft
, CShadowTree
* pRight
)
94 case CBrowseRefsDlg::eCol_Name
: return SortStrCmp(pLeft
->GetRefName(), pRight
->GetRefName());
95 case CBrowseRefsDlg::eCol_Date
: return pLeft
->m_csDate_Iso8601
.CompareNoCase(pRight
->m_csDate_Iso8601
);
96 case CBrowseRefsDlg::eCol_Msg
: return SortStrCmp(pLeft
->m_csSubject
, pRight
->m_csSubject
);
97 case CBrowseRefsDlg::eCol_LastAuthor
: return SortStrCmp(pLeft
->m_csAuthor
, pRight
->m_csAuthor
);
98 case CBrowseRefsDlg::eCol_Hash
: return pLeft
->m_csRefHash
.CompareNoCase(pRight
->m_csRefHash
);
99 case CBrowseRefsDlg::eCol_Description
: return SortStrCmp(pLeft
->m_csDescription
, pRight
->m_csDescription
);
103 int SortStrCmp(CString left
, CString right
)
106 return StrCmpLogicalW(left
, right
);
107 return StrCmpI(left
, right
);
116 // CBrowseRefsDlg dialog
118 IMPLEMENT_DYNAMIC(CBrowseRefsDlg
, CResizableStandAloneDialog
)
120 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath
, CWnd
* pParent
/*=NULL*/)
121 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD
, pParent
),
124 m_currSortDesc(false),
125 m_initialRef(L
"HEAD"),
126 m_pickRef_Kind(gPickRef_All
),
127 m_pListCtrlRoot(NULL
),
129 m_SelectedFilters(LOGFILTER_ALL
),
135 CBrowseRefsDlg::~CBrowseRefsDlg()
139 void CBrowseRefsDlg::DoDataExchange(CDataExchange
* pDX
)
141 CDialog::DoDataExchange(pDX
);
142 DDX_Control(pDX
, IDC_TREE_REF
, m_RefTreeCtrl
);
143 DDX_Control(pDX
, IDC_LIST_REF_LEAFS
, m_ListRefLeafs
);
144 DDX_Control(pDX
, IDC_BROWSEREFS_EDIT_FILTER
, m_ctrlFilter
);
148 BEGIN_MESSAGE_MAP(CBrowseRefsDlg
, CResizableStandAloneDialog
)
149 ON_BN_CLICKED(IDOK
, &CBrowseRefsDlg::OnBnClickedOk
)
150 ON_NOTIFY(TVN_SELCHANGED
, IDC_TREE_REF
, &CBrowseRefsDlg::OnTvnSelchangedTreeRef
)
152 ON_NOTIFY(LVN_COLUMNCLICK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs
)
154 ON_NOTIFY(NM_DBLCLK
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnNMDblclkListRefLeafs
)
155 ON_NOTIFY(LVN_ENDLABELEDIT
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs
)
156 ON_NOTIFY(LVN_BEGINLABELEDIT
, IDC_LIST_REF_LEAFS
, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs
)
157 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER
, &CBrowseRefsDlg::OnEnChangeEditFilter
)
158 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED
, OnClickedInfoIcon
)
163 // CBrowseRefsDlg message handlers
165 void CBrowseRefsDlg::OnBnClickedOk()
170 BOOL
CBrowseRefsDlg::OnInitDialog()
172 CResizableStandAloneDialog::OnInitDialog();
173 CAppUtils::MarkWindowAsUnpinnable(m_hWnd
);
175 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
176 m_ctrlFilter
.SetCancelBitmaps(IDI_CANCELNORMAL
, IDI_CANCELPRESSED
);
177 m_ctrlFilter
.SetInfoIcon(IDI_FILTEREDIT
);
180 AddAnchor(IDC_TREE_REF
, TOP_LEFT
, BOTTOM_LEFT
);
181 AddAnchor(IDC_LIST_REF_LEAFS
, TOP_LEFT
, BOTTOM_RIGHT
);
182 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER
, TOP_LEFT
);
183 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER
, TOP_LEFT
, TOP_RIGHT
);
184 AddAnchor(IDHELP
, BOTTOM_RIGHT
);
186 m_ListRefLeafs
.SetExtendedStyle(m_ListRefLeafs
.GetExtendedStyle()|LVS_EX_FULLROWSELECT
);
188 temp
.LoadString(IDS_BRANCHNAME
);
189 m_ListRefLeafs
.InsertColumn(eCol_Name
, temp
, 0, 150);
190 temp
.LoadString(IDS_DATELASTCOMMIT
);
191 m_ListRefLeafs
.InsertColumn(eCol_Date
, temp
, 0, 100);
192 temp
.LoadString(IDS_LASTCOMMIT
);
193 m_ListRefLeafs
.InsertColumn(eCol_Msg
, temp
, 0, 300);
194 temp
.LoadString(IDS_LASTAUTHOR
);
195 m_ListRefLeafs
.InsertColumn(eCol_LastAuthor
, temp
, 0, 100);
196 temp
.LoadString(IDS_HASH
);
197 m_ListRefLeafs
.InsertColumn(eCol_Hash
, temp
, 0, 80);
198 temp
.LoadString(IDS_DESCRIPTION
);
199 m_ListRefLeafs
.InsertColumn(eCol_Description
, temp
, 0, 80);
201 AddAnchor(IDOK
,BOTTOM_RIGHT
);
202 AddAnchor(IDCANCEL
,BOTTOM_RIGHT
);
204 Refresh(m_initialRef
);
206 EnableSaveRestore(L
"BrowseRefs");
208 CString sWindowTitle
;
209 GetWindowText(sWindowTitle
);
210 CAppUtils::SetWindowTitle(m_hWnd
, g_Git
.m_CurrentDir
, sWindowTitle
);
212 m_bHasWC
= !g_GitAdminDir
.IsBareRepo(g_Git
.m_CurrentDir
);
215 m_ListRefLeafs
.ModifyStyle(0, LVS_SINGLESEL
);
217 m_ListRefLeafs
.SetFocus();
221 CShadowTree
* CShadowTree::GetNextSub(CString
& nameLeft
, bool bCreateIfNotExist
)
223 int posSlash
=nameLeft
.Find('/');
228 nameLeft
.Empty();//Nothing left
232 nameSub
=nameLeft
.Left(posSlash
);
233 nameLeft
=nameLeft
.Mid(posSlash
+1);
235 if(nameSub
.IsEmpty())
238 if(!bCreateIfNotExist
&& m_ShadowTree
.find(nameSub
)==m_ShadowTree
.end())
241 CShadowTree
& nextNode
=m_ShadowTree
[nameSub
];
242 nextNode
.m_csRefName
=nameSub
;
243 nextNode
.m_pParent
=this;
247 CShadowTree
* CShadowTree::FindLeaf(CString partialRefName
)
251 if(m_csRefName
.GetLength() > partialRefName
.GetLength())
253 if(partialRefName
.Right(m_csRefName
.GetLength()) == m_csRefName
)
255 //Match of leaf name. Try match on total name.
256 CString totalRefName
= GetRefName();
257 if(totalRefName
.Right(partialRefName
.GetLength()) == partialRefName
)
258 return this; //Also match. Found.
263 //Not a leaf. Search all nodes.
264 for(TShadowTreeMap::iterator itShadowTree
= m_ShadowTree
.begin(); itShadowTree
!= m_ShadowTree
.end(); ++itShadowTree
)
266 CShadowTree
* pSubtree
= itShadowTree
->second
.FindLeaf(partialRefName
);
268 return pSubtree
; //Found
271 return NULL
;//Not found
275 typedef std::map
<CString
,CString
> MAP_STRING_STRING
;
277 CString
CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf
, bool pickFirstSelIfMultiSel
)
279 POSITION pos
=m_ListRefLeafs
.GetFirstSelectedItemPosition();
280 //List ctrl selection?
281 if(pos
&& (pickFirstSelIfMultiSel
|| m_ListRefLeafs
.GetSelectedCount() == 1))
284 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(
285 m_ListRefLeafs
.GetNextSelectedItem(pos
));
286 return pTree
->GetRefName();
290 //Tree ctrl selection?
291 HTREEITEM hTree
=m_RefTreeCtrl
.GetSelectedItem();
294 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTree
);
295 return pTree
->GetRefName();
298 return CString();//None
301 static int GetBranchDescriptionsCallback(const char *var_name
, const char *value
, void *data
)
303 MAP_STRING_STRING
*descriptions
= (MAP_STRING_STRING
*) data
;
304 CString key
= CUnicodeUtils::GetUnicode(var_name
, CP_UTF8
);
305 CString val
= CUnicodeUtils::GetUnicode(value
, CP_UTF8
);
306 descriptions
->insert(make_pair(key
.Mid(7, key
.GetLength() - 7 - 12), val
)); // 7: branch., 12: .description
310 MAP_STRING_STRING
GetBranchDescriptions()
312 MAP_STRING_STRING descriptions
;
314 git_config_new(&config
);
315 CStringA projectConfigA
= CUnicodeUtils::GetMulti(g_Git
.m_CurrentDir
+ _T("\\.git\\config"), CP_UTF8
);
316 git_config_add_file_ondisk(config
, projectConfigA
.GetBuffer(), 3);
317 projectConfigA
.ReleaseBuffer();
318 git_config_foreach_match(config
, "branch\\..*\\.description", GetBranchDescriptionsCallback
, &descriptions
);
319 git_config_free(config
);
323 void CBrowseRefsDlg::Refresh(CString selectRef
)
326 // g_Git.GetMapHashToFriendName(m_RefMap);
328 if(!selectRef
.IsEmpty())
330 if(selectRef
== "HEAD")
332 selectRef
= g_Git
.GetSymbolicRef(selectRef
, false);
337 selectRef
= GetSelectedRef(false, true);
340 m_RefTreeCtrl
.DeleteAllItems();
341 m_ListRefLeafs
.DeleteAllItems();
342 m_TreeRoot
.m_ShadowTree
.clear();
343 m_TreeRoot
.m_csRefName
= "refs";
344 m_TreeRoot
.m_hTree
=m_RefTreeCtrl
.InsertItem(L
"Refs",NULL
,NULL
);
345 m_RefTreeCtrl
.SetItemData(m_TreeRoot
.m_hTree
,(DWORD_PTR
)&m_TreeRoot
);
348 g_Git
.Run(L
"git for-each-ref --format="
351 L
"%(authordate:relative)%04"
354 L
"%(authordate:iso8601)%03",
355 &allRefs
, NULL
, CP_UTF8
);
360 MAP_STRING_STRING refMap
;
362 //First sort on ref name
363 while(!(singleRef
=allRefs
.Tokenize(L
"\03",linePos
)).IsEmpty())
365 singleRef
.TrimLeft(L
"\r\n");
367 CString refName
=singleRef
.Tokenize(L
"\04",valuePos
);
368 if(refName
.IsEmpty())
370 CString refRest
=singleRef
.Mid(valuePos
);
373 //Use ref based on m_pickRef_Kind
374 if(wcsncmp(refName
,L
"refs/heads",10)==0 && !(m_pickRef_Kind
& gPickRef_Head
) )
376 if(wcsncmp(refName
,L
"refs/tags",9)==0 && !(m_pickRef_Kind
& gPickRef_Tag
) )
378 if(wcsncmp(refName
,L
"refs/remotes",12)==0 && !(m_pickRef_Kind
& gPickRef_Remote
) )
381 refMap
[refName
] = refRest
; //Use
384 MAP_STRING_STRING descriptions
= GetBranchDescriptions();
387 for(MAP_STRING_STRING::iterator iterRefMap
=refMap
.begin();iterRefMap
!=refMap
.end();++iterRefMap
)
389 CShadowTree
& treeLeaf
=GetTreeNode(iterRefMap
->first
,NULL
,true);
390 CString values
=iterRefMap
->second
;
391 values
.Replace(L
"\04" L
"\04",L
"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
394 treeLeaf
.m_csRefHash
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
395 treeLeaf
.m_csDate
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
396 treeLeaf
.m_csSubject
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
397 treeLeaf
.m_csAuthor
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
398 treeLeaf
.m_csDate_Iso8601
= values
.Tokenize(L
"\04",valuePos
);
400 if (wcsncmp(iterRefMap
->first
, L
"refs/heads", 10) == 0)
401 treeLeaf
.m_csDescription
= descriptions
[treeLeaf
.m_csRefName
];
405 if(selectRef
.IsEmpty() || !SelectRef(selectRef
, false))
406 //Probably not on a branch. Select root node.
407 m_RefTreeCtrl
.Expand(m_TreeRoot
.m_hTree
,TVE_EXPAND
);
411 bool CBrowseRefsDlg::SelectRef(CString refName
, bool bExactMatch
)
415 CString newRefName
= GetFullRefName(refName
);
416 if(!newRefName
.IsEmpty())
417 refName
= newRefName
;
418 //else refName is not a valid ref. Try to select as good as possible.
420 if(_wcsnicmp(refName
, L
"refs/", 5) != 0)
421 return false; // Not a ref name
423 CShadowTree
& treeLeafHead
=GetTreeNode(refName
,NULL
,false);
424 if(treeLeafHead
.m_hTree
!= NULL
)
426 //Not a leaf. Select tree node and return
427 m_RefTreeCtrl
.Select(treeLeafHead
.m_hTree
,TVGN_CARET
);
431 if(treeLeafHead
.m_pParent
==NULL
)
432 return false; //Weird... should not occur.
434 //This is the current head.
435 m_RefTreeCtrl
.Select(treeLeafHead
.m_pParent
->m_hTree
,TVGN_CARET
);
437 for(int indexPos
= 0; indexPos
< m_ListRefLeafs
.GetItemCount(); ++indexPos
)
439 CShadowTree
* pCurrShadowTree
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(indexPos
);
440 if(pCurrShadowTree
== &treeLeafHead
)
442 m_ListRefLeafs
.SetItemState(indexPos
,LVIS_SELECTED
,LVIS_SELECTED
);
443 m_ListRefLeafs
.EnsureVisible(indexPos
,FALSE
);
450 CShadowTree
& CBrowseRefsDlg::GetTreeNode(CString refName
, CShadowTree
* pTreePos
, bool bCreateIfNotExist
)
454 if(_wcsnicmp(refName
, L
"refs/", 5) == 0)
455 refName
=refName
.Mid(5);
456 pTreePos
=&m_TreeRoot
;
458 if(refName
.IsEmpty())
459 return *pTreePos
;//Found leaf
461 CShadowTree
* pNextTree
=pTreePos
->GetNextSub(refName
,bCreateIfNotExist
);
464 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
465 ASSERT(!bCreateIfNotExist
);
469 if(!refName
.IsEmpty())
471 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
472 //Leafs are for the list control.
473 if(pNextTree
->m_hTree
==NULL
)
475 //New tree. Create node in control.
476 pNextTree
->m_hTree
=m_RefTreeCtrl
.InsertItem(pNextTree
->m_csRefName
,pTreePos
->m_hTree
,NULL
);
477 m_RefTreeCtrl
.SetItemData(pNextTree
->m_hTree
,(DWORD_PTR
)pNextTree
);
481 return GetTreeNode(refName
, pNextTree
, bCreateIfNotExist
);
485 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR
*pNMHDR
, LRESULT
*pResult
)
487 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
490 FillListCtrlForTreeNode(pNMTreeView
->itemNew
.hItem
);
493 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode
)
495 m_ListRefLeafs
.DeleteAllItems();
497 CShadowTree
* pTree
=(CShadowTree
*)(m_RefTreeCtrl
.GetItemData(treeNode
));
503 FillListCtrlForShadowTree(pTree
,L
"",true);
506 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree
* pTree
, CString refNamePrefix
, bool isFirstLevel
)
511 m_ctrlFilter
.GetWindowText(filter
);
513 CString ref
= refNamePrefix
+ pTree
->m_csRefName
;
514 if (!(pTree
->m_csRefName
.IsEmpty() || pTree
->m_csRefName
== "refs" && pTree
->m_pParent
== NULL
) && IsMatchFilter(pTree
, ref
, filter
))
516 int indexItem
= m_ListRefLeafs
.InsertItem(m_ListRefLeafs
.GetItemCount(), L
"");
518 m_ListRefLeafs
.SetItemData(indexItem
,(DWORD_PTR
)pTree
);
519 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Name
, ref
);
520 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Date
, pTree
->m_csDate
);
521 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Msg
, pTree
->m_csSubject
);
522 m_ListRefLeafs
.SetItemText(indexItem
,eCol_LastAuthor
, pTree
->m_csAuthor
);
523 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Hash
, pTree
->m_csRefHash
);
525 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Description
, pTree
->m_csDescription
.Tokenize(_T("\n"), pos
));
533 csThisName
=refNamePrefix
+pTree
->m_csRefName
+L
"/";
535 m_pListCtrlRoot
= pTree
;
536 for(CShadowTree::TShadowTreeMap::iterator itSubTree
=pTree
->m_ShadowTree
.begin(); itSubTree
!=pTree
->m_ShadowTree
.end(); ++itSubTree
)
538 FillListCtrlForShadowTree(&itSubTree
->second
,csThisName
,false);
543 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
544 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
546 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
550 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree
* pTree
, const CString
&ref
, const CString
&filter
)
552 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
555 msg
= msg
.MakeLower();
557 if (msg
.Find(filter
) >= 0)
561 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
563 CString msg
= pTree
->m_csSubject
;
564 msg
= msg
.MakeLower();
566 if (msg
.Find(filter
) >= 0)
570 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
572 CString msg
= pTree
->m_csAuthor
;
573 msg
= msg
.MakeLower();
575 if (msg
.Find(filter
) >= 0)
579 if (m_SelectedFilters
& LOGFILTER_REVS
)
581 CString msg
= pTree
->m_csRefHash
;
582 msg
= msg
.MakeLower();
584 if (msg
.Find(filter
) >= 0)
590 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree
& leafs
)
592 ASSERT(!leafs
.empty());
595 UINT mbIcon
=MB_ICONQUESTION
;
597 bool bIsRemoteBranch
= false;
598 bool bIsBranch
= false;
599 if (leafs
[0]->IsFrom(L
"refs/remotes")) {bIsBranch
= true; bIsRemoteBranch
= true;}
600 else if (leafs
[0]->IsFrom(L
"refs/heads")) {bIsBranch
= true;}
604 if(leafs
.size() == 1)
606 CString branchToDelete
= leafs
[0]->GetRefName().Mid(bIsRemoteBranch
? 13 : 11);
607 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, branchToDelete
);
609 //Check if branch is fully merged in HEAD
610 CGitHash branchHash
= g_Git
.GetHash(leafs
[0]->GetRefName());
611 CGitHash commonAncestor
;
612 CString commonAncestorstr
;
614 cmd
.Format(L
"git.exe merge-base HEAD %s", leafs
[0]->GetRefName());
615 g_Git
.Run(cmd
, &commonAncestorstr
, NULL
, CP_UTF8
);
617 commonAncestor
=commonAncestorstr
;
619 if(commonAncestor
!= branchHash
)
621 csMessage
+= L
"\r\n\r\n";
622 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED
));
623 mbIcon
= MB_ICONWARNING
;
628 csMessage
+= L
"\r\n\r\n";
629 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
630 mbIcon
= MB_ICONWARNING
;
635 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
637 csMessage
+= L
"\r\n\r\n";
638 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK
));
639 mbIcon
= MB_ICONWARNING
;
643 csMessage
+= L
"\r\n\r\n";
644 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
645 mbIcon
= MB_ICONWARNING
;
650 else if(leafs
[0]->IsFrom(L
"refs/tags"))
652 if(leafs
.size() == 1)
654 CString tagToDelete
= leafs
[0]->GetRefName().Mid(10);
655 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, tagToDelete
);
659 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
663 return CMessageBox::Show(m_hWnd
, csMessage
, _T("TortoiseGit"), MB_YESNO
| mbIcon
) == IDYES
;
667 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree
& leafs
, bool bForce
)
669 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
670 if(!DoDeleteRef((*i
)->GetRefName(), bForce
))
675 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName
, bool bForce
)
677 bool bIsRemoteBranch
= false;
678 bool bIsBranch
= false;
679 if (wcsncmp(completeRefName
, L
"refs/remotes",12)==0) {bIsBranch
= true; bIsRemoteBranch
= true;}
680 else if (wcsncmp(completeRefName
, L
"refs/heads",10)==0) {bIsBranch
= true;}
684 CString branchToDelete
= completeRefName
.Mid(bIsRemoteBranch
? 13 : 11);
688 int slash
= branchToDelete
.Find(L
'/');
691 CString remoteName
= branchToDelete
.Left(slash
);
692 CString remoteBranchToDelete
= branchToDelete
.Mid(slash
+ 1);
694 if(CAppUtils::IsSSHPutty())
696 CAppUtils::LaunchPAgent(NULL
, &remoteName
);
699 cmd
.Format(L
"git.exe push \"%s\" :refs/heads/%s", remoteName
, remoteBranchToDelete
);
702 cmd
.Format(L
"git.exe branch -%c -- %s",bForce
?L
'D':L
'd',branchToDelete
);
704 if(g_Git
.Run(cmd
,&errorMsg
,CP_UTF8
)!=0)
706 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
710 else if(wcsncmp(completeRefName
,L
"refs/tags",9)==0)
712 CString tagToDelete
= completeRefName
.Mid(10);
714 cmd
.Format(L
"git.exe tag -d -- %s",tagToDelete
);
716 if(g_Git
.Run(cmd
,&errorMsg
,CP_UTF8
)!=0)
718 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
725 CString
CBrowseRefsDlg::GetFullRefName(CString partialRefName
)
727 CShadowTree
* pLeaf
= m_TreeRoot
.FindLeaf(partialRefName
);
730 return pLeaf
->GetRefName();
734 void CBrowseRefsDlg::OnContextMenu(CWnd
* pWndFrom
, CPoint point
)
736 if(pWndFrom
==&m_RefTreeCtrl
) OnContextMenu_RefTreeCtrl(point
);
737 else if(pWndFrom
==&m_ListRefLeafs
) OnContextMenu_ListRefLeafs(point
);
740 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point
)
742 CPoint clientPoint
=point
;
743 m_RefTreeCtrl
.ScreenToClient(&clientPoint
);
745 HTREEITEM hTreeItem
=m_RefTreeCtrl
.HitTest(clientPoint
);
747 m_RefTreeCtrl
.Select(hTreeItem
,TVGN_CARET
);
749 VectorPShadowTree tree
;
750 ShowContextMenu(point
,hTreeItem
,tree
);
754 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point
)
756 std::vector
<CShadowTree
*> selectedLeafs
;
757 selectedLeafs
.reserve(m_ListRefLeafs
.GetSelectedCount());
758 POSITION pos
=m_ListRefLeafs
.GetFirstSelectedItemPosition();
761 selectedLeafs
.push_back(
762 (CShadowTree
*)m_ListRefLeafs
.GetItemData(
763 m_ListRefLeafs
.GetNextSelectedItem(pos
)));
766 ShowContextMenu(point
,m_RefTreeCtrl
.GetSelectedItem(),selectedLeafs
);
769 void CBrowseRefsDlg::ShowContextMenu(CPoint point
, HTREEITEM hTreePos
, VectorPShadowTree
& selectedLeafs
)
772 popupMenu
.CreatePopupMenu();
774 bool bAddSeparator
= false;
777 if(selectedLeafs
.size()==1)
779 bAddSeparator
= true;
781 bool bShowReflogOption
= false;
782 bool bShowFetchOption
= false;
783 bool bShowSwitchOption
= false;
784 bool bShowRenameOption
= false;
785 bool bShowCreateBranchOption
= false;
786 bool bShowEditBranchDescriptionOption
= false;
788 CString fetchFromCmd
;
790 if(selectedLeafs
[0]->IsFrom(L
"refs/heads"))
792 bShowReflogOption
= true;
793 bShowSwitchOption
= true;
794 bShowRenameOption
= true;
795 bShowEditBranchDescriptionOption
= true;
797 else if(selectedLeafs
[0]->IsFrom(L
"refs/remotes"))
799 bShowReflogOption
= true;
800 bShowFetchOption
= true;
801 bShowCreateBranchOption
= true;
803 int dummy
= 0;//Needed for tokenize
804 remoteName
= selectedLeafs
[0]->GetRefName();
805 remoteName
= remoteName
.Mid(13);
806 remoteName
= remoteName
.Tokenize(L
"/", dummy
);
807 fetchFromCmd
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
809 else if(selectedLeafs
[0]->IsFrom(L
"refs/tags"))
814 temp
.LoadString(IDS_MENULOG
);
815 popupMenu
.AppendMenuIcon(eCmd_ViewLog
, temp
, IDI_LOG
);
816 popupMenu
.AppendMenuIcon(eCmd_RepoBrowser
, IDS_LOG_BROWSEREPO
, IDI_REPOBROWSE
);
817 if(bShowReflogOption
)
819 temp
.LoadString(IDS_MENUREFLOG
);
820 popupMenu
.AppendMenuIcon(eCmd_ShowReflog
, temp
, IDI_LOG
);
823 popupMenu
.AppendMenu(MF_SEPARATOR
);
824 bAddSeparator
= false;
828 bAddSeparator
= true;
829 popupMenu
.AppendMenuIcon(eCmd_Fetch
, fetchFromCmd
, IDI_PULL
);
833 popupMenu
.AppendMenu(MF_SEPARATOR
);
835 bAddSeparator
= false;
838 popupMenu
.AppendMenuIcon(eCmd_Switch
, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS
)), IDI_SWITCH
);
839 popupMenu
.AppendMenu(MF_SEPARATOR
);
842 if(bShowCreateBranchOption
)
844 bAddSeparator
= true;
845 temp
.LoadString(IDS_MENUBRANCH
);
846 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
849 if (bShowEditBranchDescriptionOption
)
851 bAddSeparator
= true;
852 popupMenu
.AppendMenuIcon(eCmd_EditBranchDescription
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
)), IDI_RENAME
);
854 if(bShowRenameOption
)
856 bAddSeparator
= true;
857 popupMenu
.AppendMenuIcon(eCmd_Rename
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME
)), IDI_RENAME
);
860 else if(selectedLeafs
.size() == 2)
862 bAddSeparator
= true;
863 popupMenu
.AppendMenuIcon(eCmd_Diff
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS
)), IDI_DIFF
);
866 if(!selectedLeafs
.empty())
868 if(AreAllFrom(selectedLeafs
, L
"refs/remotes/"))
871 popupMenu
.AppendMenu(MF_SEPARATOR
);
872 CString menuItemName
;
873 if(selectedLeafs
.size() == 1)
874 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH
);
876 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES
, selectedLeafs
.size());
878 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteBranch
, menuItemName
, IDI_DELETE
);
879 bAddSeparator
= true;
881 else if(AreAllFrom(selectedLeafs
, L
"refs/heads/"))
884 popupMenu
.AppendMenu(MF_SEPARATOR
);
885 CString menuItemName
;
886 if(selectedLeafs
.size() == 1)
887 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH
);
889 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES
, selectedLeafs
.size());
891 popupMenu
.AppendMenuIcon(eCmd_DeleteBranch
, menuItemName
, IDI_DELETE
);
892 bAddSeparator
= true;
894 else if(AreAllFrom(selectedLeafs
, L
"refs/tags/"))
897 popupMenu
.AppendMenu(MF_SEPARATOR
);
898 CString menuItemName
;
899 if(selectedLeafs
.size() == 1)
900 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETETAG
);
902 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETETAGS
, selectedLeafs
.size());
904 popupMenu
.AppendMenuIcon(eCmd_DeleteTag
, menuItemName
, IDI_DELETE
);
905 bAddSeparator
= true;
910 if(hTreePos
!=NULL
&& selectedLeafs
.empty())
912 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTreePos
);
913 if(pTree
->IsFrom(L
"refs/remotes"))
916 popupMenu
.AppendMenu(MF_SEPARATOR
);
917 popupMenu
.AppendMenuIcon(eCmd_ManageRemotes
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES
)), IDI_SETTINGS
);
918 bAddSeparator
= true;
919 if(selectedLeafs
.empty())
921 int dummy
= 0;//Needed for tokenize
922 remoteName
= pTree
->GetRefName();
923 remoteName
= remoteName
.Mid(13);
924 remoteName
= remoteName
.Tokenize(L
"/", dummy
);
925 if(!remoteName
.IsEmpty())
928 temp
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
929 popupMenu
.AppendMenuIcon(eCmd_Fetch
, temp
, IDI_PULL
);
931 temp
.LoadString(IDS_DELETEREMOTETAG
);
932 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteTag
, temp
, IDI_DELETE
);
936 if(pTree
->IsFrom(L
"refs/heads"))
939 popupMenu
.AppendMenu(MF_SEPARATOR
);
941 temp
.LoadString(IDS_MENUBRANCH
);
942 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
944 if(pTree
->IsFrom(L
"refs/tags"))
947 popupMenu
.AppendMenu(MF_SEPARATOR
);
949 temp
.LoadString(IDS_MENUTAG
);
950 popupMenu
.AppendMenuIcon(eCmd_CreateTag
, temp
, IDI_TAG
);
955 eCmd cmd
=(eCmd
)popupMenu
.TrackPopupMenuEx(TPM_LEFTALIGN
|TPM_RETURNCMD
, point
.x
, point
.y
, this, 0);
961 dlg
.SetStartRef(selectedLeafs
[0]->GetRefName());
965 case eCmd_RepoBrowser
:
966 CAppUtils::RunTortoiseProc(_T("/command:repobrowser /path:\"") + g_Git
.m_CurrentDir
+ _T("\" /rev:") + selectedLeafs
[0]->GetRefName());
968 case eCmd_DeleteBranch
:
969 case eCmd_DeleteRemoteBranch
:
971 if(ConfirmDeleteRef(selectedLeafs
))
972 DoDeleteRefs(selectedLeafs
, true);
978 if(ConfirmDeleteRef(selectedLeafs
))
979 DoDeleteRefs(selectedLeafs
, true);
983 case eCmd_ShowReflog
:
985 CRefLogDlg
refLogDlg(this);
986 refLogDlg
.m_CurrentBranch
= selectedLeafs
[0]->GetRefName();
992 CAppUtils::Fetch(remoteName
);
996 case eCmd_DeleteRemoteTag
:
998 CDeleteRemoteTagDlg deleteRemoteTagDlg
;
999 deleteRemoteTagDlg
.m_sRemote
= remoteName
;
1000 deleteRemoteTagDlg
.DoModal();
1005 CAppUtils::Switch(selectedLeafs
[0]->GetRefName());
1010 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1012 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1015 case eCmd_AddRemote
:
1017 CAddRemoteDlg(this).DoModal();
1021 case eCmd_ManageRemotes
:
1023 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS
)), new CSettingGitRemote(g_Git
.m_CurrentDir
), this).DoModal();
1024 // CSettingGitRemote W_Remotes(m_cmdPath);
1025 // W_Remotes.DoModal();
1029 case eCmd_CreateBranch
:
1031 CString
*commitHash
= NULL
;
1032 if (selectedLeafs
.size() == 1)
1033 commitHash
= &(selectedLeafs
[0]->m_csRefHash
);
1034 CAppUtils::CreateBranchTag(false, commitHash
);
1038 case eCmd_CreateTag
:
1040 CAppUtils::CreateBranchTag(true);
1049 selectedLeafs
[0]->m_csRefHash
,
1050 selectedLeafs
[1]->m_csRefHash
);
1054 case eCmd_EditBranchDescription
:
1057 dlg
.m_sHintText
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1058 dlg
.m_sInputText
= selectedLeafs
[0]->m_csDescription
;
1059 dlg
.m_sTitle
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1060 dlg
.m_bUseLogWidth
= true;
1061 if(dlg
.DoModal() == IDOK
)
1064 key
.Format(_T("branch.%s.description"), selectedLeafs
[0]->m_csRefName
);
1065 dlg
.m_sInputText
.Replace(_T("\r"), _T(""));
1066 dlg
.m_sInputText
.Trim();
1067 if (dlg
.m_sInputText
.IsEmpty())
1068 g_Git
.UnsetConfigValue(key
);
1070 g_Git
.SetConfigValue(key
, dlg
.m_sInputText
);
1078 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree
& leafs
, const wchar_t* from
)
1080 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
1081 if(!(*i
)->IsFrom(from
))
1086 BOOL
CBrowseRefsDlg::PreTranslateMessage(MSG
* pMsg
)
1088 if (pMsg
->message
== WM_KEYDOWN
)
1090 switch (pMsg
->wParam
)
1094 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1096 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1098 PostMessage(WM_COMMAND, IDOK);
1106 if(pMsg
->hwnd
== m_ListRefLeafs
.m_hWnd
)
1108 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1110 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1124 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
1127 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1129 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1132 if(m_currSortCol
== pNMLV
->iSubItem
)
1133 m_currSortDesc
= !m_currSortDesc
;
1136 m_currSortCol
= pNMLV
->iSubItem
;
1137 m_currSortDesc
= false;
1140 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
1141 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
1143 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
1146 void CBrowseRefsDlg::OnDestroy()
1148 m_pickedRef
= GetSelectedRef(true, false);
1150 CResizableStandAloneDialog::OnDestroy();
1153 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1155 UNREFERENCED_PARAMETER(pNMHDR
);
1161 CString
CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef
, int pickRef_Kind
)
1163 CBrowseRefsDlg
dlg(CString(),NULL
);
1165 if(initialRef
.IsEmpty())
1166 initialRef
= L
"HEAD";
1167 dlg
.m_initialRef
= initialRef
;
1168 dlg
.m_pickRef_Kind
= pickRef_Kind
;
1169 dlg
.m_bPickOne
= true;
1171 if(dlg
.DoModal() != IDOK
)
1174 return dlg
.m_pickedRef
;
1177 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx
* pComboBox
, int pickRef_Kind
)
1180 pComboBox
->GetLBText(pComboBox
->GetCurSel(), origRef
);
1181 CString resultRef
= PickRef(false,origRef
,pickRef_Kind
);
1182 if(resultRef
.IsEmpty())
1184 if(wcsncmp(resultRef
,L
"refs/",5)==0)
1185 resultRef
= resultRef
.Mid(5);
1186 // if(wcsncmp(resultRef,L"heads/",6)==0)
1187 // resultRef = resultRef.Mid(6);
1189 //Find closest match of choice in combobox
1191 int matchLength
= 0;
1192 CString comboRefName
;
1193 for(int i
= 0; i
< pComboBox
->GetCount(); ++i
)
1195 pComboBox
->GetLBText(i
, comboRefName
);
1196 if(comboRefName
.Find(L
'/') < 0 && !comboRefName
.IsEmpty())
1197 comboRefName
.Insert(0,L
"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1198 if(matchLength
< comboRefName
.GetLength() && resultRef
.Right(comboRefName
.GetLength()) == comboRefName
)
1200 matchLength
= comboRefName
.GetLength();
1205 pComboBox
->SetCurSel(ixFound
);
1207 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)
1212 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1214 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1217 if(pDispInfo
->item
.pszText
== NULL
)
1218 return; //User canceled changing
1220 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1222 if(!pTree
->IsFrom(L
"refs/heads"))
1224 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1228 CString origName
= pTree
->GetRefName().Mid(11);
1231 if(m_pListCtrlRoot
!= NULL
)
1232 newName
= m_pListCtrlRoot
->GetRefName() + L
'/';
1233 newName
+= pDispInfo
->item
.pszText
;
1235 if(wcsncmp(newName
,L
"refs/heads/",11)!=0)
1237 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1241 CString newNameTrunced
= newName
.Mid(11);
1244 if(g_Git
.Run(L
"git branch -m \"" + origName
+ L
"\" \"" + newNameTrunced
+ L
"\"", &errorMsg
, CP_UTF8
) != 0)
1246 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1249 //Do as if it failed to rename. Let Refresh() do the job.
1254 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1256 // AfxMessageBox(W_csPopup);
1260 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1262 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1265 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1267 if(!pTree
->IsFrom(L
"refs/heads"))
1269 *pResult
= TRUE
; //Dont allow renaming any other things then branches at the moment.
1274 void CBrowseRefsDlg::OnEnChangeEditFilter()
1276 SetTimer(IDT_FILTER
, 1000, NULL
);
1279 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent
)
1281 if (nIDEvent
== IDT_FILTER
)
1283 KillTimer(IDT_FILTER
);
1284 FillListCtrlForTreeNode(m_RefTreeCtrl
.GetSelectedItem());
1287 CResizableStandAloneDialog::OnTimer(nIDEvent
);
1290 LRESULT
CBrowseRefsDlg::OnClickedInfoIcon(WPARAM
/*wParam*/, LPARAM lParam
)
1292 // FIXME: x64 version would get this function called with unexpected parameters.
1296 RECT
* rect
= (LPRECT
)lParam
;
1299 point
= CPoint(rect
->left
, rect
->bottom
);
1300 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1302 if (popup
.CreatePopupMenu())
1304 temp
.LoadString(IDS_LOG_FILTER_REFNAME
);
1305 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME
), LOGFILTER_REFNAME
, temp
);
1307 temp
.LoadString(IDS_LOG_FILTER_SUBJECT
);
1308 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT
), LOGFILTER_SUBJECT
, temp
);
1310 temp
.LoadString(IDS_LOG_FILTER_AUTHORS
);
1311 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS
), LOGFILTER_AUTHORS
, temp
);
1313 temp
.LoadString(IDS_LOG_FILTER_REVS
);
1314 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS
), LOGFILTER_REVS
, temp
);
1316 int selection
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
1319 m_SelectedFilters
^= selection
;
1321 SetTimer(IDT_FILTER
, 1000, NULL
);
1327 void CBrowseRefsDlg::SetFilterCueText()
1329 CString
temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY
));
1332 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
1333 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME
));
1335 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
1337 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1339 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT
));
1342 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
1344 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1346 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS
));
1349 if (m_SelectedFilters
& LOGFILTER_REVS
)
1351 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1353 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS
));
1356 // to make the cue banner text appear more to the right of the edit control
1357 temp
= _T(" ") + temp
;
1358 m_ctrlFilter
.SetCueBanner(temp
.TrimRight());