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"
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 git_config_entry
*entry
, void *data
)
303 MAP_STRING_STRING
*descriptions
= (MAP_STRING_STRING
*) data
;
304 CString key
= CUnicodeUtils::GetUnicode(entry
->name
, CP_UTF8
);
305 CString val
= CUnicodeUtils::GetUnicode(entry
->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
.GetGitLocalConfig(), CP_UTF8
);
316 git_config_add_file_ondisk(config
, projectConfigA
.GetBuffer(), 3, FALSE
);
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
);
347 CString allRefs
, error
;
348 if (g_Git
.Run(L
"git for-each-ref --format="
351 L
"%(authordate:relative)%04"
354 L
"%(authordate:iso8601)%03",
355 &allRefs
, &error
, CP_UTF8
))
357 CMessageBox::Show(NULL
, CString(_T("Get refs failed\n")) + error
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
363 MAP_STRING_STRING refMap
;
365 //First sort on ref name
366 while(!(singleRef
=allRefs
.Tokenize(L
"\03",linePos
)).IsEmpty())
368 singleRef
.TrimLeft(L
"\r\n");
370 CString refName
=singleRef
.Tokenize(L
"\04",valuePos
);
371 if(refName
.IsEmpty())
373 CString refRest
=singleRef
.Mid(valuePos
);
376 //Use ref based on m_pickRef_Kind
377 if(wcsncmp(refName
,L
"refs/heads",10)==0 && !(m_pickRef_Kind
& gPickRef_Head
) )
379 if(wcsncmp(refName
,L
"refs/tags",9)==0 && !(m_pickRef_Kind
& gPickRef_Tag
) )
381 if(wcsncmp(refName
,L
"refs/remotes",12)==0 && !(m_pickRef_Kind
& gPickRef_Remote
) )
384 refMap
[refName
] = refRest
; //Use
387 MAP_STRING_STRING descriptions
= GetBranchDescriptions();
390 for(MAP_STRING_STRING::iterator iterRefMap
=refMap
.begin();iterRefMap
!=refMap
.end();++iterRefMap
)
392 CShadowTree
& treeLeaf
=GetTreeNode(iterRefMap
->first
,NULL
,true);
393 CString values
=iterRefMap
->second
;
394 values
.Replace(L
"\04" L
"\04",L
"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
397 treeLeaf
.m_csRefHash
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
398 treeLeaf
.m_csDate
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
399 treeLeaf
.m_csSubject
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
400 treeLeaf
.m_csAuthor
= values
.Tokenize(L
"\04",valuePos
); if(valuePos
< 0) continue;
401 treeLeaf
.m_csDate_Iso8601
= values
.Tokenize(L
"\04",valuePos
);
403 if (wcsncmp(iterRefMap
->first
, L
"refs/heads", 10) == 0)
404 treeLeaf
.m_csDescription
= descriptions
[treeLeaf
.m_csRefName
];
408 if(selectRef
.IsEmpty() || !SelectRef(selectRef
, false))
409 //Probably not on a branch. Select root node.
410 m_RefTreeCtrl
.Expand(m_TreeRoot
.m_hTree
,TVE_EXPAND
);
414 bool CBrowseRefsDlg::SelectRef(CString refName
, bool bExactMatch
)
418 CString newRefName
= GetFullRefName(refName
);
419 if(!newRefName
.IsEmpty())
420 refName
= newRefName
;
421 //else refName is not a valid ref. Try to select as good as possible.
423 if(_wcsnicmp(refName
, L
"refs/", 5) != 0)
424 return false; // Not a ref name
426 CShadowTree
& treeLeafHead
=GetTreeNode(refName
,NULL
,false);
427 if(treeLeafHead
.m_hTree
!= NULL
)
429 //Not a leaf. Select tree node and return
430 m_RefTreeCtrl
.Select(treeLeafHead
.m_hTree
,TVGN_CARET
);
434 if(treeLeafHead
.m_pParent
==NULL
)
435 return false; //Weird... should not occur.
437 //This is the current head.
438 m_RefTreeCtrl
.Select(treeLeafHead
.m_pParent
->m_hTree
,TVGN_CARET
);
440 for(int indexPos
= 0; indexPos
< m_ListRefLeafs
.GetItemCount(); ++indexPos
)
442 CShadowTree
* pCurrShadowTree
= (CShadowTree
*)m_ListRefLeafs
.GetItemData(indexPos
);
443 if(pCurrShadowTree
== &treeLeafHead
)
445 m_ListRefLeafs
.SetItemState(indexPos
,LVIS_SELECTED
,LVIS_SELECTED
);
446 m_ListRefLeafs
.EnsureVisible(indexPos
,FALSE
);
453 CShadowTree
& CBrowseRefsDlg::GetTreeNode(CString refName
, CShadowTree
* pTreePos
, bool bCreateIfNotExist
)
457 if(_wcsnicmp(refName
, L
"refs/", 5) == 0)
458 refName
=refName
.Mid(5);
459 pTreePos
=&m_TreeRoot
;
461 if(refName
.IsEmpty())
462 return *pTreePos
;//Found leaf
464 CShadowTree
* pNextTree
=pTreePos
->GetNextSub(refName
,bCreateIfNotExist
);
467 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
468 ASSERT(!bCreateIfNotExist
);
472 if(!refName
.IsEmpty())
474 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
475 //Leafs are for the list control.
476 if(pNextTree
->m_hTree
==NULL
)
478 //New tree. Create node in control.
479 pNextTree
->m_hTree
=m_RefTreeCtrl
.InsertItem(pNextTree
->m_csRefName
,pTreePos
->m_hTree
,NULL
);
480 m_RefTreeCtrl
.SetItemData(pNextTree
->m_hTree
,(DWORD_PTR
)pNextTree
);
484 return GetTreeNode(refName
, pNextTree
, bCreateIfNotExist
);
488 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR
*pNMHDR
, LRESULT
*pResult
)
490 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
493 FillListCtrlForTreeNode(pNMTreeView
->itemNew
.hItem
);
496 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode
)
498 m_ListRefLeafs
.DeleteAllItems();
500 CShadowTree
* pTree
=(CShadowTree
*)(m_RefTreeCtrl
.GetItemData(treeNode
));
506 FillListCtrlForShadowTree(pTree
,L
"",true);
509 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree
* pTree
, CString refNamePrefix
, bool isFirstLevel
)
514 m_ctrlFilter
.GetWindowText(filter
);
516 CString ref
= refNamePrefix
+ pTree
->m_csRefName
;
517 if (!(pTree
->m_csRefName
.IsEmpty() || pTree
->m_csRefName
== "refs" && pTree
->m_pParent
== NULL
) && IsMatchFilter(pTree
, ref
, filter
))
519 int indexItem
= m_ListRefLeafs
.InsertItem(m_ListRefLeafs
.GetItemCount(), L
"");
521 m_ListRefLeafs
.SetItemData(indexItem
,(DWORD_PTR
)pTree
);
522 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Name
, ref
);
523 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Date
, pTree
->m_csDate
);
524 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Msg
, pTree
->m_csSubject
);
525 m_ListRefLeafs
.SetItemText(indexItem
,eCol_LastAuthor
, pTree
->m_csAuthor
);
526 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Hash
, pTree
->m_csRefHash
);
528 m_ListRefLeafs
.SetItemText(indexItem
,eCol_Description
, pTree
->m_csDescription
.Tokenize(_T("\n"), pos
));
536 csThisName
=refNamePrefix
+pTree
->m_csRefName
+L
"/";
538 m_pListCtrlRoot
= pTree
;
539 for(CShadowTree::TShadowTreeMap::iterator itSubTree
=pTree
->m_ShadowTree
.begin(); itSubTree
!=pTree
->m_ShadowTree
.end(); ++itSubTree
)
541 FillListCtrlForShadowTree(&itSubTree
->second
,csThisName
,false);
546 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
547 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
549 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
553 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree
* pTree
, const CString
&ref
, const CString
&filter
)
555 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
558 msg
= msg
.MakeLower();
560 if (msg
.Find(filter
) >= 0)
564 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
566 CString msg
= pTree
->m_csSubject
;
567 msg
= msg
.MakeLower();
569 if (msg
.Find(filter
) >= 0)
573 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
575 CString msg
= pTree
->m_csAuthor
;
576 msg
= msg
.MakeLower();
578 if (msg
.Find(filter
) >= 0)
582 if (m_SelectedFilters
& LOGFILTER_REVS
)
584 CString msg
= pTree
->m_csRefHash
;
585 msg
= msg
.MakeLower();
587 if (msg
.Find(filter
) >= 0)
593 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree
& leafs
)
595 ASSERT(!leafs
.empty());
598 UINT mbIcon
=MB_ICONQUESTION
;
600 bool bIsRemoteBranch
= false;
601 bool bIsBranch
= false;
602 if (leafs
[0]->IsFrom(L
"refs/remotes")) {bIsBranch
= true; bIsRemoteBranch
= true;}
603 else if (leafs
[0]->IsFrom(L
"refs/heads")) {bIsBranch
= true;}
607 if(leafs
.size() == 1)
609 CString branchToDelete
= leafs
[0]->GetRefName().Mid(bIsRemoteBranch
? 13 : 11);
610 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, branchToDelete
);
612 //Check if branch is fully merged in HEAD
613 if (!g_Git
.IsFastForward(leafs
[0]->GetRefName(), _T("HEAD")))
615 csMessage
+= L
"\r\n\r\n";
616 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED
));
617 mbIcon
= MB_ICONWARNING
;
622 csMessage
+= L
"\r\n\r\n";
623 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
624 mbIcon
= MB_ICONWARNING
;
629 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
631 csMessage
+= L
"\r\n\r\n";
632 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK
));
633 mbIcon
= MB_ICONWARNING
;
637 csMessage
+= L
"\r\n\r\n";
638 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
639 mbIcon
= MB_ICONWARNING
;
644 else if(leafs
[0]->IsFrom(L
"refs/tags"))
646 if(leafs
.size() == 1)
648 CString tagToDelete
= leafs
[0]->GetRefName().Mid(10);
649 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, tagToDelete
);
653 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
657 return CMessageBox::Show(m_hWnd
, csMessage
, _T("TortoiseGit"), MB_YESNO
| mbIcon
) == IDYES
;
661 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree
& leafs
, bool bForce
)
663 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
664 if(!DoDeleteRef((*i
)->GetRefName(), bForce
))
669 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName
, bool bForce
)
671 bool bIsRemoteBranch
= false;
672 bool bIsBranch
= false;
673 if (wcsncmp(completeRefName
, L
"refs/remotes",12)==0) {bIsBranch
= true; bIsRemoteBranch
= true;}
674 else if (wcsncmp(completeRefName
, L
"refs/heads",10)==0) {bIsBranch
= true;}
678 CString branchToDelete
= completeRefName
.Mid(bIsRemoteBranch
? 13 : 11);
682 int slash
= branchToDelete
.Find(L
'/');
685 CString remoteName
= branchToDelete
.Left(slash
);
686 CString remoteBranchToDelete
= branchToDelete
.Mid(slash
+ 1);
688 if(CAppUtils::IsSSHPutty())
690 CAppUtils::LaunchPAgent(NULL
, &remoteName
);
693 cmd
.Format(L
"git.exe push \"%s\" :refs/heads/%s", remoteName
, remoteBranchToDelete
);
696 cmd
.Format(L
"git.exe branch -%c -- %s",bForce
?L
'D':L
'd',branchToDelete
);
698 if(g_Git
.Run(cmd
,&errorMsg
,CP_UTF8
)!=0)
700 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
704 else if(wcsncmp(completeRefName
,L
"refs/tags",9)==0)
706 CString tagToDelete
= completeRefName
.Mid(10);
708 cmd
.Format(L
"git.exe tag -d -- %s",tagToDelete
);
710 if(g_Git
.Run(cmd
,&errorMsg
,CP_UTF8
)!=0)
712 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
719 CString
CBrowseRefsDlg::GetFullRefName(CString partialRefName
)
721 CShadowTree
* pLeaf
= m_TreeRoot
.FindLeaf(partialRefName
);
724 return pLeaf
->GetRefName();
728 void CBrowseRefsDlg::OnContextMenu(CWnd
* pWndFrom
, CPoint point
)
730 if(pWndFrom
==&m_RefTreeCtrl
) OnContextMenu_RefTreeCtrl(point
);
731 else if(pWndFrom
==&m_ListRefLeafs
) OnContextMenu_ListRefLeafs(point
);
734 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point
)
736 CPoint clientPoint
=point
;
737 m_RefTreeCtrl
.ScreenToClient(&clientPoint
);
739 HTREEITEM hTreeItem
=m_RefTreeCtrl
.HitTest(clientPoint
);
741 m_RefTreeCtrl
.Select(hTreeItem
,TVGN_CARET
);
743 VectorPShadowTree tree
;
744 ShowContextMenu(point
,hTreeItem
,tree
);
748 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point
)
750 std::vector
<CShadowTree
*> selectedLeafs
;
751 selectedLeafs
.reserve(m_ListRefLeafs
.GetSelectedCount());
752 POSITION pos
=m_ListRefLeafs
.GetFirstSelectedItemPosition();
755 selectedLeafs
.push_back(
756 (CShadowTree
*)m_ListRefLeafs
.GetItemData(
757 m_ListRefLeafs
.GetNextSelectedItem(pos
)));
760 ShowContextMenu(point
,m_RefTreeCtrl
.GetSelectedItem(),selectedLeafs
);
763 void CBrowseRefsDlg::ShowContextMenu(CPoint point
, HTREEITEM hTreePos
, VectorPShadowTree
& selectedLeafs
)
766 popupMenu
.CreatePopupMenu();
768 bool bAddSeparator
= false;
771 if(selectedLeafs
.size()==1)
773 bAddSeparator
= true;
775 bool bShowReflogOption
= false;
776 bool bShowFetchOption
= false;
777 bool bShowSwitchOption
= false;
778 bool bShowRenameOption
= false;
779 bool bShowCreateBranchOption
= false;
780 bool bShowEditBranchDescriptionOption
= false;
782 CString fetchFromCmd
;
784 if(selectedLeafs
[0]->IsFrom(L
"refs/heads"))
786 bShowReflogOption
= true;
787 bShowSwitchOption
= true;
788 bShowRenameOption
= true;
789 bShowEditBranchDescriptionOption
= true;
791 else if(selectedLeafs
[0]->IsFrom(L
"refs/remotes"))
793 bShowReflogOption
= true;
794 bShowFetchOption
= true;
795 bShowCreateBranchOption
= true;
797 int dummy
= 0;//Needed for tokenize
798 remoteName
= selectedLeafs
[0]->GetRefName();
799 remoteName
= remoteName
.Mid(13);
800 remoteName
= remoteName
.Tokenize(L
"/", dummy
);
801 fetchFromCmd
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
803 else if(selectedLeafs
[0]->IsFrom(L
"refs/tags"))
808 temp
.LoadString(IDS_MENULOG
);
809 popupMenu
.AppendMenuIcon(eCmd_ViewLog
, temp
, IDI_LOG
);
810 popupMenu
.AppendMenuIcon(eCmd_RepoBrowser
, IDS_LOG_BROWSEREPO
, IDI_REPOBROWSE
);
811 if(bShowReflogOption
)
813 temp
.LoadString(IDS_MENUREFLOG
);
814 popupMenu
.AppendMenuIcon(eCmd_ShowReflog
, temp
, IDI_LOG
);
817 popupMenu
.AppendMenu(MF_SEPARATOR
);
818 bAddSeparator
= false;
822 bAddSeparator
= true;
823 popupMenu
.AppendMenuIcon(eCmd_Fetch
, fetchFromCmd
, IDI_PULL
);
827 popupMenu
.AppendMenu(MF_SEPARATOR
);
829 bAddSeparator
= false;
832 popupMenu
.AppendMenuIcon(eCmd_Switch
, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS
)), IDI_SWITCH
);
833 popupMenu
.AppendMenu(MF_SEPARATOR
);
836 if(bShowCreateBranchOption
)
838 bAddSeparator
= true;
839 temp
.LoadString(IDS_MENUBRANCH
);
840 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
843 if (bShowEditBranchDescriptionOption
)
845 bAddSeparator
= true;
846 popupMenu
.AppendMenuIcon(eCmd_EditBranchDescription
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
)), IDI_RENAME
);
848 if(bShowRenameOption
)
850 bAddSeparator
= true;
851 popupMenu
.AppendMenuIcon(eCmd_Rename
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME
)), IDI_RENAME
);
854 else if(selectedLeafs
.size() == 2)
856 bAddSeparator
= true;
857 popupMenu
.AppendMenuIcon(eCmd_Diff
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS
)), IDI_DIFF
);
860 if(!selectedLeafs
.empty())
862 if(AreAllFrom(selectedLeafs
, L
"refs/remotes/"))
865 popupMenu
.AppendMenu(MF_SEPARATOR
);
866 CString menuItemName
;
867 if(selectedLeafs
.size() == 1)
868 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH
);
870 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES
, selectedLeafs
.size());
872 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteBranch
, menuItemName
, IDI_DELETE
);
873 bAddSeparator
= true;
875 else if(AreAllFrom(selectedLeafs
, L
"refs/heads/"))
878 popupMenu
.AppendMenu(MF_SEPARATOR
);
879 CString menuItemName
;
880 if(selectedLeafs
.size() == 1)
881 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH
);
883 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES
, selectedLeafs
.size());
885 popupMenu
.AppendMenuIcon(eCmd_DeleteBranch
, menuItemName
, IDI_DELETE
);
886 bAddSeparator
= true;
888 else if(AreAllFrom(selectedLeafs
, L
"refs/tags/"))
891 popupMenu
.AppendMenu(MF_SEPARATOR
);
892 CString menuItemName
;
893 if(selectedLeafs
.size() == 1)
894 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETETAG
);
896 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETETAGS
, selectedLeafs
.size());
898 popupMenu
.AppendMenuIcon(eCmd_DeleteTag
, menuItemName
, IDI_DELETE
);
899 bAddSeparator
= true;
904 if(hTreePos
!=NULL
&& selectedLeafs
.empty())
906 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTreePos
);
907 if(pTree
->IsFrom(L
"refs/remotes"))
910 popupMenu
.AppendMenu(MF_SEPARATOR
);
911 popupMenu
.AppendMenuIcon(eCmd_ManageRemotes
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES
)), IDI_SETTINGS
);
912 bAddSeparator
= true;
913 if(selectedLeafs
.empty())
915 int dummy
= 0;//Needed for tokenize
916 remoteName
= pTree
->GetRefName();
917 remoteName
= remoteName
.Mid(13);
918 remoteName
= remoteName
.Tokenize(L
"/", dummy
);
919 if(!remoteName
.IsEmpty())
922 temp
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
923 popupMenu
.AppendMenuIcon(eCmd_Fetch
, temp
, IDI_PULL
);
925 temp
.LoadString(IDS_DELETEREMOTETAG
);
926 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteTag
, temp
, IDI_DELETE
);
930 if(pTree
->IsFrom(L
"refs/heads"))
933 popupMenu
.AppendMenu(MF_SEPARATOR
);
935 temp
.LoadString(IDS_MENUBRANCH
);
936 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
938 if(pTree
->IsFrom(L
"refs/tags"))
941 popupMenu
.AppendMenu(MF_SEPARATOR
);
943 temp
.LoadString(IDS_MENUTAG
);
944 popupMenu
.AppendMenuIcon(eCmd_CreateTag
, temp
, IDI_TAG
);
949 eCmd cmd
=(eCmd
)popupMenu
.TrackPopupMenuEx(TPM_LEFTALIGN
|TPM_RETURNCMD
, point
.x
, point
.y
, this, 0);
955 dlg
.SetStartRef(selectedLeafs
[0]->GetRefName());
959 case eCmd_RepoBrowser
:
960 CAppUtils::RunTortoiseGitProc(_T("/command:repobrowser /path:\"") + g_Git
.m_CurrentDir
+ _T("\" /rev:") + selectedLeafs
[0]->GetRefName());
962 case eCmd_DeleteBranch
:
963 case eCmd_DeleteRemoteBranch
:
965 if(ConfirmDeleteRef(selectedLeafs
))
966 DoDeleteRefs(selectedLeafs
, true);
972 if(ConfirmDeleteRef(selectedLeafs
))
973 DoDeleteRefs(selectedLeafs
, true);
977 case eCmd_ShowReflog
:
979 CRefLogDlg
refLogDlg(this);
980 refLogDlg
.m_CurrentBranch
= selectedLeafs
[0]->GetRefName();
986 CAppUtils::Fetch(remoteName
);
990 case eCmd_DeleteRemoteTag
:
992 CDeleteRemoteTagDlg deleteRemoteTagDlg
;
993 deleteRemoteTagDlg
.m_sRemote
= remoteName
;
994 deleteRemoteTagDlg
.DoModal();
999 CAppUtils::Switch(selectedLeafs
[0]->GetRefName());
1004 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1006 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1009 case eCmd_AddRemote
:
1011 CAddRemoteDlg(this).DoModal();
1015 case eCmd_ManageRemotes
:
1017 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS
)), new CSettingGitRemote(g_Git
.m_CurrentDir
), this).DoModal();
1018 // CSettingGitRemote W_Remotes(m_cmdPath);
1019 // W_Remotes.DoModal();
1023 case eCmd_CreateBranch
:
1025 CString
*commitHash
= NULL
;
1026 if (selectedLeafs
.size() == 1)
1027 commitHash
= &(selectedLeafs
[0]->m_csRefHash
);
1028 CAppUtils::CreateBranchTag(false, commitHash
);
1032 case eCmd_CreateTag
:
1034 CAppUtils::CreateBranchTag(true);
1043 selectedLeafs
[0]->m_csRefHash
,
1044 selectedLeafs
[1]->m_csRefHash
);
1048 case eCmd_EditBranchDescription
:
1051 dlg
.m_sHintText
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1052 dlg
.m_sInputText
= selectedLeafs
[0]->m_csDescription
;
1053 dlg
.m_sTitle
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1054 dlg
.m_bUseLogWidth
= true;
1055 if(dlg
.DoModal() == IDOK
)
1058 key
.Format(_T("branch.%s.description"), selectedLeafs
[0]->m_csRefName
);
1059 dlg
.m_sInputText
.Replace(_T("\r"), _T(""));
1060 dlg
.m_sInputText
.Trim();
1061 if (dlg
.m_sInputText
.IsEmpty())
1062 g_Git
.UnsetConfigValue(key
);
1064 g_Git
.SetConfigValue(key
, dlg
.m_sInputText
);
1072 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree
& leafs
, const wchar_t* from
)
1074 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
1075 if(!(*i
)->IsFrom(from
))
1080 BOOL
CBrowseRefsDlg::PreTranslateMessage(MSG
* pMsg
)
1082 if (pMsg
->message
== WM_KEYDOWN
)
1084 switch (pMsg
->wParam
)
1088 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1090 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1092 PostMessage(WM_COMMAND, IDOK);
1100 if(pMsg
->hwnd
== m_ListRefLeafs
.m_hWnd
)
1102 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1104 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1118 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
1121 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1123 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1126 if(m_currSortCol
== pNMLV
->iSubItem
)
1127 m_currSortDesc
= !m_currSortDesc
;
1130 m_currSortCol
= pNMLV
->iSubItem
;
1131 m_currSortDesc
= false;
1134 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
1135 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
1137 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
1140 void CBrowseRefsDlg::OnDestroy()
1142 m_pickedRef
= GetSelectedRef(true, false);
1144 CResizableStandAloneDialog::OnDestroy();
1147 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
1154 CString
CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef
, int pickRef_Kind
)
1156 CBrowseRefsDlg
dlg(CString(),NULL
);
1158 if(initialRef
.IsEmpty())
1159 initialRef
= L
"HEAD";
1160 dlg
.m_initialRef
= initialRef
;
1161 dlg
.m_pickRef_Kind
= pickRef_Kind
;
1162 dlg
.m_bPickOne
= true;
1164 if(dlg
.DoModal() != IDOK
)
1167 return dlg
.m_pickedRef
;
1170 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx
* pComboBox
, int pickRef_Kind
)
1173 pComboBox
->GetLBText(pComboBox
->GetCurSel(), origRef
);
1174 CString resultRef
= PickRef(false,origRef
,pickRef_Kind
);
1175 if(resultRef
.IsEmpty())
1177 if(wcsncmp(resultRef
,L
"refs/",5)==0)
1178 resultRef
= resultRef
.Mid(5);
1179 // if(wcsncmp(resultRef,L"heads/",6)==0)
1180 // resultRef = resultRef.Mid(6);
1182 //Find closest match of choice in combobox
1184 int matchLength
= 0;
1185 CString comboRefName
;
1186 for(int i
= 0; i
< pComboBox
->GetCount(); ++i
)
1188 pComboBox
->GetLBText(i
, comboRefName
);
1189 if(comboRefName
.Find(L
'/') < 0 && !comboRefName
.IsEmpty())
1190 comboRefName
.Insert(0,L
"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1191 if(matchLength
< comboRefName
.GetLength() && resultRef
.Right(comboRefName
.GetLength()) == comboRefName
)
1193 matchLength
= comboRefName
.GetLength();
1198 pComboBox
->SetCurSel(ixFound
);
1200 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)
1205 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1207 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1210 if(pDispInfo
->item
.pszText
== NULL
)
1211 return; //User canceled changing
1213 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1215 if(!pTree
->IsFrom(L
"refs/heads"))
1217 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1221 CString origName
= pTree
->GetRefName().Mid(11);
1224 if(m_pListCtrlRoot
!= NULL
)
1225 newName
= m_pListCtrlRoot
->GetRefName() + L
'/';
1226 newName
+= pDispInfo
->item
.pszText
;
1228 if(wcsncmp(newName
,L
"refs/heads/",11)!=0)
1230 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1234 CString newNameTrunced
= newName
.Mid(11);
1237 if(g_Git
.Run(L
"git branch -m \"" + origName
+ L
"\" \"" + newNameTrunced
+ L
"\"", &errorMsg
, CP_UTF8
) != 0)
1239 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1242 //Do as if it failed to rename. Let Refresh() do the job.
1247 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1249 // AfxMessageBox(W_csPopup);
1253 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1255 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1258 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1260 if(!pTree
->IsFrom(L
"refs/heads"))
1262 *pResult
= TRUE
; //Dont allow renaming any other things then branches at the moment.
1267 void CBrowseRefsDlg::OnEnChangeEditFilter()
1269 SetTimer(IDT_FILTER
, 1000, NULL
);
1272 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent
)
1274 if (nIDEvent
== IDT_FILTER
)
1276 KillTimer(IDT_FILTER
);
1277 FillListCtrlForTreeNode(m_RefTreeCtrl
.GetSelectedItem());
1280 CResizableStandAloneDialog::OnTimer(nIDEvent
);
1283 LRESULT
CBrowseRefsDlg::OnClickedInfoIcon(WPARAM
/*wParam*/, LPARAM lParam
)
1285 // FIXME: x64 version would get this function called with unexpected parameters.
1289 RECT
* rect
= (LPRECT
)lParam
;
1292 point
= CPoint(rect
->left
, rect
->bottom
);
1293 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1295 if (popup
.CreatePopupMenu())
1297 temp
.LoadString(IDS_LOG_FILTER_REFNAME
);
1298 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME
), LOGFILTER_REFNAME
, temp
);
1300 temp
.LoadString(IDS_LOG_FILTER_SUBJECT
);
1301 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT
), LOGFILTER_SUBJECT
, temp
);
1303 temp
.LoadString(IDS_LOG_FILTER_AUTHORS
);
1304 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS
), LOGFILTER_AUTHORS
, temp
);
1306 temp
.LoadString(IDS_LOG_FILTER_REVS
);
1307 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS
), LOGFILTER_REVS
, temp
);
1309 int selection
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
1312 m_SelectedFilters
^= selection
;
1314 SetTimer(IDT_FILTER
, 1000, NULL
);
1320 void CBrowseRefsDlg::SetFilterCueText()
1322 CString
temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY
));
1325 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
1326 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME
));
1328 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
1330 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1332 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT
));
1335 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
1337 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1339 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS
));
1342 if (m_SelectedFilters
& LOGFILTER_REVS
)
1344 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1346 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS
));
1349 // to make the cue banner text appear more to the right of the edit control
1350 temp
= _T(" ") + temp
;
1351 m_ctrlFilter
.SetCueBanner(temp
.TrimRight());