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 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
);
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 if (!g_Git
.IsFastForward(leafs
[0]->GetRefName(), _T("HEAD")))
612 csMessage
+= L
"\r\n\r\n";
613 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED
));
614 mbIcon
= MB_ICONWARNING
;
619 csMessage
+= L
"\r\n\r\n";
620 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
621 mbIcon
= MB_ICONWARNING
;
626 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
628 csMessage
+= L
"\r\n\r\n";
629 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK
));
630 mbIcon
= MB_ICONWARNING
;
634 csMessage
+= L
"\r\n\r\n";
635 csMessage
+= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES
));
636 mbIcon
= MB_ICONWARNING
;
641 else if(leafs
[0]->IsFrom(L
"refs/tags"))
643 if(leafs
.size() == 1)
645 CString tagToDelete
= leafs
[0]->GetRefName().Mid(10);
646 csMessage
.Format(IDS_PROC_DELETEBRANCHTAG
, tagToDelete
);
650 csMessage
.Format(IDS_PROC_DELETENREFS
, leafs
.size());
654 return CMessageBox::Show(m_hWnd
, csMessage
, _T("TortoiseGit"), MB_YESNO
| mbIcon
) == IDYES
;
658 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree
& leafs
, bool bForce
)
660 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
661 if(!DoDeleteRef((*i
)->GetRefName(), bForce
))
666 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName
, bool bForce
)
668 bool bIsRemoteBranch
= false;
669 bool bIsBranch
= false;
670 if (wcsncmp(completeRefName
, L
"refs/remotes",12)==0) {bIsBranch
= true; bIsRemoteBranch
= true;}
671 else if (wcsncmp(completeRefName
, L
"refs/heads",10)==0) {bIsBranch
= true;}
675 CString branchToDelete
= completeRefName
.Mid(bIsRemoteBranch
? 13 : 11);
679 int slash
= branchToDelete
.Find(L
'/');
682 CString remoteName
= branchToDelete
.Left(slash
);
683 CString remoteBranchToDelete
= branchToDelete
.Mid(slash
+ 1);
685 if(CAppUtils::IsSSHPutty())
687 CAppUtils::LaunchPAgent(NULL
, &remoteName
);
690 cmd
.Format(L
"git.exe push \"%s\" :refs/heads/%s", remoteName
, remoteBranchToDelete
);
693 cmd
.Format(L
"git.exe branch -%c -- %s",bForce
?L
'D':L
'd',branchToDelete
);
695 if(g_Git
.Run(cmd
,&errorMsg
,CP_UTF8
)!=0)
697 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
701 else if(wcsncmp(completeRefName
,L
"refs/tags",9)==0)
703 CString tagToDelete
= completeRefName
.Mid(10);
705 cmd
.Format(L
"git.exe tag -d -- %s",tagToDelete
);
707 if(g_Git
.Run(cmd
,&errorMsg
,CP_UTF8
)!=0)
709 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
716 CString
CBrowseRefsDlg::GetFullRefName(CString partialRefName
)
718 CShadowTree
* pLeaf
= m_TreeRoot
.FindLeaf(partialRefName
);
721 return pLeaf
->GetRefName();
725 void CBrowseRefsDlg::OnContextMenu(CWnd
* pWndFrom
, CPoint point
)
727 if(pWndFrom
==&m_RefTreeCtrl
) OnContextMenu_RefTreeCtrl(point
);
728 else if(pWndFrom
==&m_ListRefLeafs
) OnContextMenu_ListRefLeafs(point
);
731 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point
)
733 CPoint clientPoint
=point
;
734 m_RefTreeCtrl
.ScreenToClient(&clientPoint
);
736 HTREEITEM hTreeItem
=m_RefTreeCtrl
.HitTest(clientPoint
);
738 m_RefTreeCtrl
.Select(hTreeItem
,TVGN_CARET
);
740 VectorPShadowTree tree
;
741 ShowContextMenu(point
,hTreeItem
,tree
);
745 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point
)
747 std::vector
<CShadowTree
*> selectedLeafs
;
748 selectedLeafs
.reserve(m_ListRefLeafs
.GetSelectedCount());
749 POSITION pos
=m_ListRefLeafs
.GetFirstSelectedItemPosition();
752 selectedLeafs
.push_back(
753 (CShadowTree
*)m_ListRefLeafs
.GetItemData(
754 m_ListRefLeafs
.GetNextSelectedItem(pos
)));
757 ShowContextMenu(point
,m_RefTreeCtrl
.GetSelectedItem(),selectedLeafs
);
760 void CBrowseRefsDlg::ShowContextMenu(CPoint point
, HTREEITEM hTreePos
, VectorPShadowTree
& selectedLeafs
)
763 popupMenu
.CreatePopupMenu();
765 bool bAddSeparator
= false;
768 if(selectedLeafs
.size()==1)
770 bAddSeparator
= true;
772 bool bShowReflogOption
= false;
773 bool bShowFetchOption
= false;
774 bool bShowSwitchOption
= false;
775 bool bShowRenameOption
= false;
776 bool bShowCreateBranchOption
= false;
777 bool bShowEditBranchDescriptionOption
= false;
779 CString fetchFromCmd
;
781 if(selectedLeafs
[0]->IsFrom(L
"refs/heads"))
783 bShowReflogOption
= true;
784 bShowSwitchOption
= true;
785 bShowRenameOption
= true;
786 bShowEditBranchDescriptionOption
= true;
788 else if(selectedLeafs
[0]->IsFrom(L
"refs/remotes"))
790 bShowReflogOption
= true;
791 bShowFetchOption
= true;
792 bShowCreateBranchOption
= true;
794 int dummy
= 0;//Needed for tokenize
795 remoteName
= selectedLeafs
[0]->GetRefName();
796 remoteName
= remoteName
.Mid(13);
797 remoteName
= remoteName
.Tokenize(L
"/", dummy
);
798 fetchFromCmd
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
800 else if(selectedLeafs
[0]->IsFrom(L
"refs/tags"))
805 temp
.LoadString(IDS_MENULOG
);
806 popupMenu
.AppendMenuIcon(eCmd_ViewLog
, temp
, IDI_LOG
);
807 popupMenu
.AppendMenuIcon(eCmd_RepoBrowser
, IDS_LOG_BROWSEREPO
, IDI_REPOBROWSE
);
808 if(bShowReflogOption
)
810 temp
.LoadString(IDS_MENUREFLOG
);
811 popupMenu
.AppendMenuIcon(eCmd_ShowReflog
, temp
, IDI_LOG
);
814 popupMenu
.AppendMenu(MF_SEPARATOR
);
815 bAddSeparator
= false;
819 bAddSeparator
= true;
820 popupMenu
.AppendMenuIcon(eCmd_Fetch
, fetchFromCmd
, IDI_PULL
);
824 popupMenu
.AppendMenu(MF_SEPARATOR
);
826 bAddSeparator
= false;
829 popupMenu
.AppendMenuIcon(eCmd_Switch
, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS
)), IDI_SWITCH
);
830 popupMenu
.AppendMenu(MF_SEPARATOR
);
833 if(bShowCreateBranchOption
)
835 bAddSeparator
= true;
836 temp
.LoadString(IDS_MENUBRANCH
);
837 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
840 if (bShowEditBranchDescriptionOption
)
842 bAddSeparator
= true;
843 popupMenu
.AppendMenuIcon(eCmd_EditBranchDescription
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
)), IDI_RENAME
);
845 if(bShowRenameOption
)
847 bAddSeparator
= true;
848 popupMenu
.AppendMenuIcon(eCmd_Rename
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME
)), IDI_RENAME
);
851 else if(selectedLeafs
.size() == 2)
853 bAddSeparator
= true;
854 popupMenu
.AppendMenuIcon(eCmd_Diff
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS
)), IDI_DIFF
);
857 if(!selectedLeafs
.empty())
859 if(AreAllFrom(selectedLeafs
, L
"refs/remotes/"))
862 popupMenu
.AppendMenu(MF_SEPARATOR
);
863 CString menuItemName
;
864 if(selectedLeafs
.size() == 1)
865 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH
);
867 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES
, selectedLeafs
.size());
869 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteBranch
, menuItemName
, IDI_DELETE
);
870 bAddSeparator
= true;
872 else if(AreAllFrom(selectedLeafs
, L
"refs/heads/"))
875 popupMenu
.AppendMenu(MF_SEPARATOR
);
876 CString menuItemName
;
877 if(selectedLeafs
.size() == 1)
878 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH
);
880 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES
, selectedLeafs
.size());
882 popupMenu
.AppendMenuIcon(eCmd_DeleteBranch
, menuItemName
, IDI_DELETE
);
883 bAddSeparator
= true;
885 else if(AreAllFrom(selectedLeafs
, L
"refs/tags/"))
888 popupMenu
.AppendMenu(MF_SEPARATOR
);
889 CString menuItemName
;
890 if(selectedLeafs
.size() == 1)
891 menuItemName
.LoadString(IDS_PROC_BROWSEREFS_DELETETAG
);
893 menuItemName
.Format(IDS_PROC_BROWSEREFS_DELETETAGS
, selectedLeafs
.size());
895 popupMenu
.AppendMenuIcon(eCmd_DeleteTag
, menuItemName
, IDI_DELETE
);
896 bAddSeparator
= true;
901 if(hTreePos
!=NULL
&& selectedLeafs
.empty())
903 CShadowTree
* pTree
=(CShadowTree
*)m_RefTreeCtrl
.GetItemData(hTreePos
);
904 if(pTree
->IsFrom(L
"refs/remotes"))
907 popupMenu
.AppendMenu(MF_SEPARATOR
);
908 popupMenu
.AppendMenuIcon(eCmd_ManageRemotes
, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES
)), IDI_SETTINGS
);
909 bAddSeparator
= true;
910 if(selectedLeafs
.empty())
912 int dummy
= 0;//Needed for tokenize
913 remoteName
= pTree
->GetRefName();
914 remoteName
= remoteName
.Mid(13);
915 remoteName
= remoteName
.Tokenize(L
"/", dummy
);
916 if(!remoteName
.IsEmpty())
919 temp
.Format(IDS_PROC_BROWSEREFS_FETCHFROM
, remoteName
);
920 popupMenu
.AppendMenuIcon(eCmd_Fetch
, temp
, IDI_PULL
);
922 temp
.LoadString(IDS_DELETEREMOTETAG
);
923 popupMenu
.AppendMenuIcon(eCmd_DeleteRemoteTag
, temp
, IDI_DELETE
);
927 if(pTree
->IsFrom(L
"refs/heads"))
930 popupMenu
.AppendMenu(MF_SEPARATOR
);
932 temp
.LoadString(IDS_MENUBRANCH
);
933 popupMenu
.AppendMenuIcon(eCmd_CreateBranch
, temp
, IDI_COPY
);
935 if(pTree
->IsFrom(L
"refs/tags"))
938 popupMenu
.AppendMenu(MF_SEPARATOR
);
940 temp
.LoadString(IDS_MENUTAG
);
941 popupMenu
.AppendMenuIcon(eCmd_CreateTag
, temp
, IDI_TAG
);
946 eCmd cmd
=(eCmd
)popupMenu
.TrackPopupMenuEx(TPM_LEFTALIGN
|TPM_RETURNCMD
, point
.x
, point
.y
, this, 0);
952 dlg
.SetStartRef(selectedLeafs
[0]->GetRefName());
956 case eCmd_RepoBrowser
:
957 CAppUtils::RunTortoiseProc(_T("/command:repobrowser /path:\"") + g_Git
.m_CurrentDir
+ _T("\" /rev:") + selectedLeafs
[0]->GetRefName());
959 case eCmd_DeleteBranch
:
960 case eCmd_DeleteRemoteBranch
:
962 if(ConfirmDeleteRef(selectedLeafs
))
963 DoDeleteRefs(selectedLeafs
, true);
969 if(ConfirmDeleteRef(selectedLeafs
))
970 DoDeleteRefs(selectedLeafs
, true);
974 case eCmd_ShowReflog
:
976 CRefLogDlg
refLogDlg(this);
977 refLogDlg
.m_CurrentBranch
= selectedLeafs
[0]->GetRefName();
983 CAppUtils::Fetch(remoteName
);
987 case eCmd_DeleteRemoteTag
:
989 CDeleteRemoteTagDlg deleteRemoteTagDlg
;
990 deleteRemoteTagDlg
.m_sRemote
= remoteName
;
991 deleteRemoteTagDlg
.DoModal();
996 CAppUtils::Switch(selectedLeafs
[0]->GetRefName());
1001 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1003 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1006 case eCmd_AddRemote
:
1008 CAddRemoteDlg(this).DoModal();
1012 case eCmd_ManageRemotes
:
1014 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS
)), new CSettingGitRemote(g_Git
.m_CurrentDir
), this).DoModal();
1015 // CSettingGitRemote W_Remotes(m_cmdPath);
1016 // W_Remotes.DoModal();
1020 case eCmd_CreateBranch
:
1022 CString
*commitHash
= NULL
;
1023 if (selectedLeafs
.size() == 1)
1024 commitHash
= &(selectedLeafs
[0]->m_csRefHash
);
1025 CAppUtils::CreateBranchTag(false, commitHash
);
1029 case eCmd_CreateTag
:
1031 CAppUtils::CreateBranchTag(true);
1040 selectedLeafs
[0]->m_csRefHash
,
1041 selectedLeafs
[1]->m_csRefHash
);
1045 case eCmd_EditBranchDescription
:
1048 dlg
.m_sHintText
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1049 dlg
.m_sInputText
= selectedLeafs
[0]->m_csDescription
;
1050 dlg
.m_sTitle
= CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION
));
1051 dlg
.m_bUseLogWidth
= true;
1052 if(dlg
.DoModal() == IDOK
)
1055 key
.Format(_T("branch.%s.description"), selectedLeafs
[0]->m_csRefName
);
1056 dlg
.m_sInputText
.Replace(_T("\r"), _T(""));
1057 dlg
.m_sInputText
.Trim();
1058 if (dlg
.m_sInputText
.IsEmpty())
1059 g_Git
.UnsetConfigValue(key
);
1061 g_Git
.SetConfigValue(key
, dlg
.m_sInputText
);
1069 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree
& leafs
, const wchar_t* from
)
1071 for(VectorPShadowTree::iterator i
= leafs
.begin(); i
!= leafs
.end(); ++i
)
1072 if(!(*i
)->IsFrom(from
))
1077 BOOL
CBrowseRefsDlg::PreTranslateMessage(MSG
* pMsg
)
1079 if (pMsg
->message
== WM_KEYDOWN
)
1081 switch (pMsg
->wParam
)
1085 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1087 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1089 PostMessage(WM_COMMAND, IDOK);
1097 if(pMsg
->hwnd
== m_ListRefLeafs
.m_hWnd
)
1099 POSITION pos
= m_ListRefLeafs
.GetFirstSelectedItemPosition();
1101 m_ListRefLeafs
.EditLabel(m_ListRefLeafs
.GetNextSelectedItem(pos
));
1115 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
1118 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1120 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1123 if(m_currSortCol
== pNMLV
->iSubItem
)
1124 m_currSortDesc
= !m_currSortDesc
;
1127 m_currSortCol
= pNMLV
->iSubItem
;
1128 m_currSortDesc
= false;
1131 CRefLeafListCompareFunc
compareFunc(&m_ListRefLeafs
, m_currSortCol
, m_currSortDesc
);
1132 m_ListRefLeafs
.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
1134 SetSortArrow(&m_ListRefLeafs
,m_currSortCol
,!m_currSortDesc
);
1137 void CBrowseRefsDlg::OnDestroy()
1139 m_pickedRef
= GetSelectedRef(true, false);
1141 CResizableStandAloneDialog::OnDestroy();
1144 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1146 UNREFERENCED_PARAMETER(pNMHDR
);
1152 CString
CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef
, int pickRef_Kind
)
1154 CBrowseRefsDlg
dlg(CString(),NULL
);
1156 if(initialRef
.IsEmpty())
1157 initialRef
= L
"HEAD";
1158 dlg
.m_initialRef
= initialRef
;
1159 dlg
.m_pickRef_Kind
= pickRef_Kind
;
1160 dlg
.m_bPickOne
= true;
1162 if(dlg
.DoModal() != IDOK
)
1165 return dlg
.m_pickedRef
;
1168 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx
* pComboBox
, int pickRef_Kind
)
1171 pComboBox
->GetLBText(pComboBox
->GetCurSel(), origRef
);
1172 CString resultRef
= PickRef(false,origRef
,pickRef_Kind
);
1173 if(resultRef
.IsEmpty())
1175 if(wcsncmp(resultRef
,L
"refs/",5)==0)
1176 resultRef
= resultRef
.Mid(5);
1177 // if(wcsncmp(resultRef,L"heads/",6)==0)
1178 // resultRef = resultRef.Mid(6);
1180 //Find closest match of choice in combobox
1182 int matchLength
= 0;
1183 CString comboRefName
;
1184 for(int i
= 0; i
< pComboBox
->GetCount(); ++i
)
1186 pComboBox
->GetLBText(i
, comboRefName
);
1187 if(comboRefName
.Find(L
'/') < 0 && !comboRefName
.IsEmpty())
1188 comboRefName
.Insert(0,L
"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1189 if(matchLength
< comboRefName
.GetLength() && resultRef
.Right(comboRefName
.GetLength()) == comboRefName
)
1191 matchLength
= comboRefName
.GetLength();
1196 pComboBox
->SetCurSel(ixFound
);
1198 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)
1203 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1205 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1208 if(pDispInfo
->item
.pszText
== NULL
)
1209 return; //User canceled changing
1211 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1213 if(!pTree
->IsFrom(L
"refs/heads"))
1215 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1219 CString origName
= pTree
->GetRefName().Mid(11);
1222 if(m_pListCtrlRoot
!= NULL
)
1223 newName
= m_pListCtrlRoot
->GetRefName() + L
'/';
1224 newName
+= pDispInfo
->item
.pszText
;
1226 if(wcsncmp(newName
,L
"refs/heads/",11)!=0)
1228 CMessageBox::Show(m_hWnd
, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1232 CString newNameTrunced
= newName
.Mid(11);
1235 if(g_Git
.Run(L
"git branch -m \"" + origName
+ L
"\" \"" + newNameTrunced
+ L
"\"", &errorMsg
, CP_UTF8
) != 0)
1237 CMessageBox::Show(m_hWnd
, errorMsg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1240 //Do as if it failed to rename. Let Refresh() do the job.
1245 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1247 // AfxMessageBox(W_csPopup);
1251 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1253 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1256 CShadowTree
* pTree
=(CShadowTree
*)m_ListRefLeafs
.GetItemData(pDispInfo
->item
.iItem
);
1258 if(!pTree
->IsFrom(L
"refs/heads"))
1260 *pResult
= TRUE
; //Dont allow renaming any other things then branches at the moment.
1265 void CBrowseRefsDlg::OnEnChangeEditFilter()
1267 SetTimer(IDT_FILTER
, 1000, NULL
);
1270 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent
)
1272 if (nIDEvent
== IDT_FILTER
)
1274 KillTimer(IDT_FILTER
);
1275 FillListCtrlForTreeNode(m_RefTreeCtrl
.GetSelectedItem());
1278 CResizableStandAloneDialog::OnTimer(nIDEvent
);
1281 LRESULT
CBrowseRefsDlg::OnClickedInfoIcon(WPARAM
/*wParam*/, LPARAM lParam
)
1283 // FIXME: x64 version would get this function called with unexpected parameters.
1287 RECT
* rect
= (LPRECT
)lParam
;
1290 point
= CPoint(rect
->left
, rect
->bottom
);
1291 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1293 if (popup
.CreatePopupMenu())
1295 temp
.LoadString(IDS_LOG_FILTER_REFNAME
);
1296 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME
), LOGFILTER_REFNAME
, temp
);
1298 temp
.LoadString(IDS_LOG_FILTER_SUBJECT
);
1299 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT
), LOGFILTER_SUBJECT
, temp
);
1301 temp
.LoadString(IDS_LOG_FILTER_AUTHORS
);
1302 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS
), LOGFILTER_AUTHORS
, temp
);
1304 temp
.LoadString(IDS_LOG_FILTER_REVS
);
1305 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS
), LOGFILTER_REVS
, temp
);
1307 int selection
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
1310 m_SelectedFilters
^= selection
;
1312 SetTimer(IDT_FILTER
, 1000, NULL
);
1318 void CBrowseRefsDlg::SetFilterCueText()
1320 CString
temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY
));
1323 if (m_SelectedFilters
& LOGFILTER_REFNAME
)
1324 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME
));
1326 if (m_SelectedFilters
& LOGFILTER_SUBJECT
)
1328 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1330 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT
));
1333 if (m_SelectedFilters
& LOGFILTER_AUTHORS
)
1335 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1337 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS
));
1340 if (m_SelectedFilters
& LOGFILTER_REVS
)
1342 if (temp
.ReverseFind(_T(' ')) != temp
.GetLength() - 1)
1344 temp
+= CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS
));
1347 // to make the cue banner text appear more to the right of the edit control
1348 temp
= _T(" ") + temp
;
1349 m_ctrlFilter
.SetCueBanner(temp
.TrimRight());